gossipcat 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -55,6 +55,9 @@ var init_mcp_context = __esm({
55
55
  pendingConsensusRounds: /* @__PURE__ */ new Map(),
56
56
  nativeUtilityConfig: null,
57
57
  mainProvider: "google",
58
+ httpMcpPort: null,
59
+ relayPortSource: null,
60
+ httpMcpPortSource: null,
58
61
  booted: false,
59
62
  boot: async () => {
60
63
  throw new Error("boot not initialized");
@@ -69,6 +72,46 @@ var init_mcp_context = __esm({
69
72
  }
70
73
  });
71
74
 
75
+ // apps/cli/src/version.ts
76
+ var version_exports = {};
77
+ __export(version_exports, {
78
+ getGossipcatVersion: () => getGossipcatVersion
79
+ });
80
+ function getGossipcatVersion() {
81
+ if (cached2 !== null) return cached2;
82
+ cached2 = resolveVersion();
83
+ return cached2;
84
+ }
85
+ function resolveVersion() {
86
+ let dir = __dirname;
87
+ const root = (0, import_path.resolve)("/");
88
+ for (let i = 0; i < 20 && dir !== root; i++) {
89
+ const candidate = (0, import_path.resolve)(dir, "package.json");
90
+ if ((0, import_fs.existsSync)(candidate)) {
91
+ try {
92
+ const pkg = JSON.parse((0, import_fs.readFileSync)(candidate, "utf-8"));
93
+ if (pkg && pkg.name === "gossipcat" && typeof pkg.version === "string") {
94
+ return pkg.version;
95
+ }
96
+ } catch {
97
+ }
98
+ }
99
+ const parent = (0, import_path.dirname)(dir);
100
+ if (parent === dir) break;
101
+ dir = parent;
102
+ }
103
+ return "unknown";
104
+ }
105
+ var import_fs, import_path, cached2;
106
+ var init_version = __esm({
107
+ "apps/cli/src/version.ts"() {
108
+ "use strict";
109
+ import_fs = require("fs");
110
+ import_path = require("path");
111
+ cached2 = null;
112
+ }
113
+ });
114
+
72
115
  // packages/orchestrator/src/llm-client.ts
73
116
  async function fetchWithRetry503(url2, init, providerName) {
74
117
  const res = await fetch(url2, init);
@@ -108,13 +151,13 @@ function createProvider(provider, model, apiKey, projectRoot, baseUrl) {
108
151
  throw new Error(`Unknown provider: ${provider}`);
109
152
  }
110
153
  }
111
- var import_crypto, import_fs, import_path, QuotaExhaustedException, QuotaTracker, AnthropicProvider, OpenAIProvider, GeminiProvider, OllamaProvider, NullProvider;
154
+ var import_crypto, import_fs2, import_path2, QuotaExhaustedException, QuotaTracker, AnthropicProvider, OpenAIProvider, GeminiProvider, OllamaProvider, NullProvider;
112
155
  var init_llm_client = __esm({
113
156
  "packages/orchestrator/src/llm-client.ts"() {
114
157
  "use strict";
115
158
  import_crypto = require("crypto");
116
- import_fs = require("fs");
117
- import_path = require("path");
159
+ import_fs2 = require("fs");
160
+ import_path2 = require("path");
118
161
  QuotaExhaustedException = class extends Error {
119
162
  provider;
120
163
  retryAfterMs;
@@ -128,7 +171,7 @@ var init_llm_client = __esm({
128
171
  QuotaTracker = class {
129
172
  constructor(provider, projectRoot) {
130
173
  this.provider = provider;
131
- this.statePath = projectRoot ? (0, import_path.join)(projectRoot, ".gossip", "quota-state.json") : null;
174
+ this.statePath = projectRoot ? (0, import_path2.join)(projectRoot, ".gossip", "quota-state.json") : null;
132
175
  this.load();
133
176
  }
134
177
  consecutive429s = 0;
@@ -136,9 +179,9 @@ var init_llm_client = __esm({
136
179
  reason = "quota";
137
180
  statePath;
138
181
  load() {
139
- if (!this.statePath || !(0, import_fs.existsSync)(this.statePath)) return;
182
+ if (!this.statePath || !(0, import_fs2.existsSync)(this.statePath)) return;
140
183
  try {
141
- const state = JSON.parse((0, import_fs.readFileSync)(this.statePath, "utf-8"));
184
+ const state = JSON.parse((0, import_fs2.readFileSync)(this.statePath, "utf-8"));
142
185
  if (state[this.provider]) {
143
186
  this.exhaustedUntil = state[this.provider].exhaustedUntil;
144
187
  this.consecutive429s = state[this.provider].consecutive429s;
@@ -150,15 +193,15 @@ var init_llm_client = __esm({
150
193
  persist() {
151
194
  if (!this.statePath) return;
152
195
  try {
153
- const dir = (0, import_path.join)(this.statePath, "..");
154
- if (!(0, import_fs.existsSync)(dir)) (0, import_fs.mkdirSync)(dir, { recursive: true });
196
+ const dir = (0, import_path2.join)(this.statePath, "..");
197
+ if (!(0, import_fs2.existsSync)(dir)) (0, import_fs2.mkdirSync)(dir, { recursive: true });
155
198
  let existing = {};
156
199
  try {
157
- existing = JSON.parse((0, import_fs.readFileSync)(this.statePath, "utf-8"));
200
+ existing = JSON.parse((0, import_fs2.readFileSync)(this.statePath, "utf-8"));
158
201
  } catch {
159
202
  }
160
203
  existing[this.provider] = { exhaustedUntil: this.exhaustedUntil, consecutive429s: this.consecutive429s, reason: this.reason };
161
- (0, import_fs.writeFileSync)(this.statePath, JSON.stringify(existing, null, 2));
204
+ (0, import_fs2.writeFileSync)(this.statePath, JSON.stringify(existing, null, 2));
162
205
  } catch {
163
206
  }
164
207
  }
@@ -3078,7 +3121,7 @@ var init_gossip_agent = __esm({
3078
3121
  }
3079
3122
  // ─── Public API ─────────────────────────────────────────────────────────────
3080
3123
  connect() {
3081
- return new Promise((resolve17, reject) => {
3124
+ return new Promise((resolve18, reject) => {
3082
3125
  const ws = new import_ws.default(this.config.relayUrl);
3083
3126
  const timeout = setTimeout(() => {
3084
3127
  ws.removeAllListeners();
@@ -3111,7 +3154,7 @@ var init_gossip_agent = __esm({
3111
3154
  ws.on("error", (err) => this.emit("error", err));
3112
3155
  this.startKeepAlive();
3113
3156
  this.emit("connect", msg.sessionId);
3114
- resolve17();
3157
+ resolve18();
3115
3158
  } else if (msg.type === "error") {
3116
3159
  clearTimeout(timeout);
3117
3160
  ws.removeAllListeners();
@@ -3137,7 +3180,7 @@ var init_gossip_agent = __esm({
3137
3180
  this.reconnectTimer = null;
3138
3181
  }
3139
3182
  if (!this.ws) return;
3140
- return new Promise((resolve17) => {
3183
+ return new Promise((resolve18) => {
3141
3184
  this.intentionalDisconnect = true;
3142
3185
  this._connected = false;
3143
3186
  const ws = this.ws;
@@ -3148,7 +3191,7 @@ var init_gossip_agent = __esm({
3148
3191
  settled = true;
3149
3192
  this.intentionalDisconnect = false;
3150
3193
  this.emit("disconnect", code);
3151
- resolve17();
3194
+ resolve18();
3152
3195
  };
3153
3196
  const timer = setTimeout(() => done(1e3), 2e3);
3154
3197
  ws.once("close", (code) => {
@@ -3187,8 +3230,8 @@ var init_gossip_agent = __esm({
3187
3230
  throw new Error("Not connected to relay");
3188
3231
  }
3189
3232
  const encoded = Buffer.from(this.codec.encode(envelope));
3190
- return new Promise((resolve17, reject) => {
3191
- this.ws.send(encoded, (err) => err ? reject(err) : resolve17());
3233
+ return new Promise((resolve18, reject) => {
3234
+ this.ws.send(encoded, (err) => err ? reject(err) : resolve18());
3192
3235
  });
3193
3236
  }
3194
3237
  // ─── Internal ────────────────────────────────────────────────────────────────
@@ -3632,7 +3675,7 @@ ${context}` : ""}
3632
3675
  this.toolCallBudget.set(name, used + 1);
3633
3676
  }
3634
3677
  const requestId = (0, import_crypto4.randomUUID)();
3635
- const resultPromise = new Promise((resolve17, reject) => {
3678
+ const resultPromise = new Promise((resolve18, reject) => {
3636
3679
  const timer = setTimeout(() => {
3637
3680
  if (this.pendingToolCalls.has(requestId)) {
3638
3681
  this.pendingToolCalls.delete(requestId);
@@ -3643,7 +3686,7 @@ ${context}` : ""}
3643
3686
  this.pendingToolCalls.set(requestId, {
3644
3687
  resolve: (r) => {
3645
3688
  clearTimeout(timer);
3646
- resolve17(r);
3689
+ resolve18(r);
3647
3690
  },
3648
3691
  reject: (e) => {
3649
3692
  clearTimeout(timer);
@@ -3708,12 +3751,12 @@ ${context}` : ""}
3708
3751
  });
3709
3752
 
3710
3753
  // packages/tools/src/file-tools.ts
3711
- var import_promises, import_path2, FileTools;
3754
+ var import_promises, import_path3, FileTools;
3712
3755
  var init_file_tools = __esm({
3713
3756
  "packages/tools/src/file-tools.ts"() {
3714
3757
  "use strict";
3715
3758
  import_promises = require("fs/promises");
3716
- import_path2 = require("path");
3759
+ import_path3 = require("path");
3717
3760
  FileTools = class {
3718
3761
  constructor(sandbox) {
3719
3762
  this.sandbox = sandbox;
@@ -3738,7 +3781,7 @@ var init_file_tools = __esm({
3738
3781
  }
3739
3782
  async fileWrite(args) {
3740
3783
  const absPath = this.sandbox.validatePath(args.path);
3741
- const dir = (0, import_path2.resolve)(absPath, "..");
3784
+ const dir = (0, import_path3.resolve)(absPath, "..");
3742
3785
  await (0, import_promises.mkdir)(dir, { recursive: true });
3743
3786
  await (0, import_promises.writeFile)(absPath, args.content, "utf-8");
3744
3787
  return `Written ${args.content.length} bytes to ${args.path}`;
@@ -3783,7 +3826,7 @@ var init_file_tools = __esm({
3783
3826
  }
3784
3827
  for (const entry of entries) {
3785
3828
  if (entry === "node_modules" || entry === ".git") continue;
3786
- const fullPath = (0, import_path2.join)(dir, entry);
3829
+ const fullPath = (0, import_path3.join)(dir, entry);
3787
3830
  let info;
3788
3831
  try {
3789
3832
  info = await (0, import_promises.stat)(fullPath);
@@ -3795,7 +3838,7 @@ var init_file_tools = __esm({
3795
3838
  } else {
3796
3839
  const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
3797
3840
  const regex = new RegExp(regexStr);
3798
- const relPath = (0, import_path2.relative)(this.sandbox.projectRoot, fullPath);
3841
+ const relPath = (0, import_path3.relative)(this.sandbox.projectRoot, fullPath);
3799
3842
  if (regex.test(entry) || regex.test(relPath)) {
3800
3843
  results.push(relPath);
3801
3844
  }
@@ -3812,7 +3855,7 @@ var init_file_tools = __esm({
3812
3855
  }
3813
3856
  for (const entry of entries) {
3814
3857
  if (entry === "node_modules" || entry === ".git") continue;
3815
- const fullPath = (0, import_path2.join)(dir, entry);
3858
+ const fullPath = (0, import_path3.join)(dir, entry);
3816
3859
  let info;
3817
3860
  try {
3818
3861
  info = await (0, import_promises.stat)(fullPath);
@@ -3825,7 +3868,7 @@ var init_file_tools = __esm({
3825
3868
  try {
3826
3869
  const content = await (0, import_promises.readFile)(fullPath, "utf-8");
3827
3870
  const lines = content.split("\n");
3828
- const relPath = (0, import_path2.relative)(this.sandbox.projectRoot, fullPath);
3871
+ const relPath = (0, import_path3.relative)(this.sandbox.projectRoot, fullPath);
3829
3872
  lines.forEach((line, idx) => {
3830
3873
  if (regex.test(line)) {
3831
3874
  results.push(`${relPath}:${idx + 1}: ${line}`);
@@ -3849,7 +3892,7 @@ var init_file_tools = __esm({
3849
3892
  const entry = filtered[i];
3850
3893
  const isLast = i === filtered.length - 1;
3851
3894
  const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
3852
- const fullPath = (0, import_path2.join)(dir, entry);
3895
+ const fullPath = (0, import_path3.join)(dir, entry);
3853
3896
  let info;
3854
3897
  try {
3855
3898
  info = await (0, import_promises.stat)(fullPath);
@@ -4054,20 +4097,20 @@ var init_git_tools = __esm({
4054
4097
  });
4055
4098
 
4056
4099
  // packages/tools/src/skill-tools.ts
4057
- var import_fs2, import_path3, SkillTools;
4100
+ var import_fs3, import_path4, SkillTools;
4058
4101
  var init_skill_tools = __esm({
4059
4102
  "packages/tools/src/skill-tools.ts"() {
4060
4103
  "use strict";
4061
- import_fs2 = require("fs");
4062
- import_path3 = require("path");
4104
+ import_fs3 = require("fs");
4105
+ import_path4 = require("path");
4063
4106
  SkillTools = class {
4064
4107
  gapLogPath;
4065
4108
  constructor(projectRoot) {
4066
- const gossipDir2 = (0, import_path3.join)(projectRoot, ".gossip");
4067
- if (!(0, import_fs2.existsSync)(gossipDir2)) {
4068
- (0, import_fs2.mkdirSync)(gossipDir2, { recursive: true });
4109
+ const gossipDir2 = (0, import_path4.join)(projectRoot, ".gossip");
4110
+ if (!(0, import_fs3.existsSync)(gossipDir2)) {
4111
+ (0, import_fs3.mkdirSync)(gossipDir2, { recursive: true });
4069
4112
  }
4070
- this.gapLogPath = (0, import_path3.join)(gossipDir2, "skill-gaps.jsonl");
4113
+ this.gapLogPath = (0, import_path4.join)(gossipDir2, "skill-gaps.jsonl");
4071
4114
  }
4072
4115
  async suggestSkill(args, callerId) {
4073
4116
  const entry = {
@@ -4078,16 +4121,16 @@ var init_skill_tools = __esm({
4078
4121
  task_context: args.task_context,
4079
4122
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
4080
4123
  };
4081
- (0, import_fs2.appendFileSync)(this.gapLogPath, JSON.stringify(entry) + "\n");
4124
+ (0, import_fs3.appendFileSync)(this.gapLogPath, JSON.stringify(entry) + "\n");
4082
4125
  this.truncateIfNeeded();
4083
4126
  return `Suggestion noted: '${args.skill_name}'. Continue with your current skills.`;
4084
4127
  }
4085
4128
  truncateIfNeeded() {
4086
4129
  try {
4087
- const content = (0, import_fs2.readFileSync)(this.gapLogPath, "utf-8");
4130
+ const content = (0, import_fs3.readFileSync)(this.gapLogPath, "utf-8");
4088
4131
  const lines = content.trim().split("\n").filter(Boolean);
4089
4132
  if (lines.length > 5e3) {
4090
- (0, import_fs2.writeFileSync)(this.gapLogPath, lines.slice(-1e3).join("\n") + "\n");
4133
+ (0, import_fs3.writeFileSync)(this.gapLogPath, lines.slice(-1e3).join("\n") + "\n");
4091
4134
  }
4092
4135
  } catch {
4093
4136
  }
@@ -4097,16 +4140,16 @@ var init_skill_tools = __esm({
4097
4140
  });
4098
4141
 
4099
4142
  // packages/tools/src/sandbox.ts
4100
- var import_path4, import_fs3, Sandbox;
4143
+ var import_path5, import_fs4, Sandbox;
4101
4144
  var init_sandbox = __esm({
4102
4145
  "packages/tools/src/sandbox.ts"() {
4103
4146
  "use strict";
4104
- import_path4 = require("path");
4105
- import_fs3 = require("fs");
4147
+ import_path5 = require("path");
4148
+ import_fs4 = require("fs");
4106
4149
  Sandbox = class {
4107
4150
  root;
4108
4151
  constructor(projectRoot) {
4109
- this.root = (0, import_fs3.realpathSync)((0, import_path4.resolve)(projectRoot));
4152
+ this.root = (0, import_fs4.realpathSync)((0, import_path5.resolve)(projectRoot));
4110
4153
  }
4111
4154
  get projectRoot() {
4112
4155
  return this.root;
@@ -4118,14 +4161,14 @@ var init_sandbox = __esm({
4118
4161
  * Resolves symlinks to prevent symlink escape attacks.
4119
4162
  */
4120
4163
  validatePath(filePath) {
4121
- const resolved = (0, import_path4.resolve)(this.root, filePath);
4164
+ const resolved = (0, import_path5.resolve)(this.root, filePath);
4122
4165
  let checkPath = resolved;
4123
- while (!(0, import_fs3.existsSync)(checkPath)) {
4124
- const parent = (0, import_path4.dirname)(checkPath);
4166
+ while (!(0, import_fs4.existsSync)(checkPath)) {
4167
+ const parent = (0, import_path5.dirname)(checkPath);
4125
4168
  if (parent === checkPath) break;
4126
4169
  checkPath = parent;
4127
4170
  }
4128
- const real = (0, import_fs3.existsSync)(checkPath) ? (0, import_fs3.realpathSync)(checkPath) : checkPath;
4171
+ const real = (0, import_fs4.existsSync)(checkPath) ? (0, import_fs4.realpathSync)(checkPath) : checkPath;
4129
4172
  const remainder = resolved.slice(checkPath.length);
4130
4173
  const fullReal = real + remainder;
4131
4174
  if (!fullReal.startsWith(this.root + "/") && fullReal !== this.root) {
@@ -8343,16 +8386,16 @@ var init_tool_schemas = __esm({
8343
8386
  // packages/tools/src/tool-server.ts
8344
8387
  function canonicalizeForBoundary(p) {
8345
8388
  let out = p;
8346
- if ((0, import_fs4.existsSync)(out)) {
8389
+ if ((0, import_fs5.existsSync)(out)) {
8347
8390
  try {
8348
- out = (0, import_fs4.realpathSync)(out);
8391
+ out = (0, import_fs5.realpathSync)(out);
8349
8392
  } catch {
8350
8393
  }
8351
8394
  } else {
8352
- const parent = (0, import_path5.dirname)(out);
8353
- if (parent !== out && (0, import_fs4.existsSync)(parent)) {
8395
+ const parent = (0, import_path6.dirname)(out);
8396
+ if (parent !== out && (0, import_fs5.existsSync)(parent)) {
8354
8397
  try {
8355
- out = (0, import_path5.join)((0, import_fs4.realpathSync)(parent), (0, import_path5.basename)(out));
8398
+ out = (0, import_path6.join)((0, import_fs5.realpathSync)(parent), (0, import_path6.basename)(out));
8356
8399
  } catch {
8357
8400
  }
8358
8401
  }
@@ -8385,7 +8428,7 @@ function truncateAtLine(text, maxLength) {
8385
8428
  const cut = text.lastIndexOf("\n", maxLength);
8386
8429
  return text.slice(0, cut !== -1 ? cut : maxLength);
8387
8430
  }
8388
- var import_msgpack4, import_crypto5, import_path5, import_fs4, CASE_INSENSITIVE_FS, ToolServer;
8431
+ var import_msgpack4, import_crypto5, import_path6, import_fs5, CASE_INSENSITIVE_FS, ToolServer;
8389
8432
  var init_tool_server = __esm({
8390
8433
  "packages/tools/src/tool-server.ts"() {
8391
8434
  "use strict";
@@ -8399,8 +8442,8 @@ var init_tool_server = __esm({
8399
8442
  init_skill_tools();
8400
8443
  init_sandbox();
8401
8444
  init_tool_schemas();
8402
- import_path5 = require("path");
8403
- import_fs4 = require("fs");
8445
+ import_path6 = require("path");
8446
+ import_fs5 = require("fs");
8404
8447
  CASE_INSENSITIVE_FS = process.platform === "darwin" || process.platform === "win32";
8405
8448
  ToolServer = class _ToolServer {
8406
8449
  agent;
@@ -8455,12 +8498,12 @@ var init_tool_server = __esm({
8455
8498
  return this.agent.agentId;
8456
8499
  }
8457
8500
  assignScope(agentId, scope) {
8458
- const abs = (0, import_path5.resolve)(this.sandbox.projectRoot, scope);
8501
+ const abs = (0, import_path6.resolve)(this.sandbox.projectRoot, scope);
8459
8502
  this.agentScopes.set(agentId, canonicalizeForBoundary(abs));
8460
8503
  this.writeAgents.add(agentId);
8461
8504
  }
8462
8505
  assignRoot(agentId, root) {
8463
- const abs = (0, import_path5.resolve)(root);
8506
+ const abs = (0, import_path6.resolve)(root);
8464
8507
  this.agentRoots.set(agentId, canonicalizeForBoundary(abs));
8465
8508
  this.writeAgents.add(agentId);
8466
8509
  }
@@ -8520,13 +8563,13 @@ var init_tool_server = __esm({
8520
8563
  if (toolName === "file_write") {
8521
8564
  const filePath = args.path;
8522
8565
  if (scope) {
8523
- const canonical = canonicalizeForBoundary((0, import_path5.resolve)(this.sandbox.projectRoot, filePath));
8566
+ const canonical = canonicalizeForBoundary((0, import_path6.resolve)(this.sandbox.projectRoot, filePath));
8524
8567
  if (!canonical.startsWith(scope)) {
8525
8568
  throw new Error(`Write blocked: "${filePath}" is outside scope "${scope}"`);
8526
8569
  }
8527
8570
  }
8528
8571
  if (root) {
8529
- const canonical = canonicalizeForBoundary((0, import_path5.resolve)(root, filePath));
8572
+ const canonical = canonicalizeForBoundary((0, import_path6.resolve)(root, filePath));
8530
8573
  if (!canonical.startsWith(root)) {
8531
8574
  throw new Error(`Write blocked: "${filePath}" is outside worktree root "${root}"`);
8532
8575
  }
@@ -8561,13 +8604,13 @@ var init_tool_server = __esm({
8561
8604
  if (toolName === "file_delete") {
8562
8605
  const filePath = args.path;
8563
8606
  if (scope) {
8564
- const canonical = canonicalizeForBoundary((0, import_path5.resolve)(this.sandbox.projectRoot, filePath));
8607
+ const canonical = canonicalizeForBoundary((0, import_path6.resolve)(this.sandbox.projectRoot, filePath));
8565
8608
  if (!canonical.startsWith(scope)) {
8566
8609
  throw new Error(`Delete blocked: "${filePath}" is outside scope "${scope}"`);
8567
8610
  }
8568
8611
  }
8569
8612
  if (root) {
8570
- const canonical = canonicalizeForBoundary((0, import_path5.resolve)(root, filePath));
8613
+ const canonical = canonicalizeForBoundary((0, import_path6.resolve)(root, filePath));
8571
8614
  if (!canonical.startsWith(root)) {
8572
8615
  throw new Error(`Delete blocked: "${filePath}" is outside worktree root "${root}"`);
8573
8616
  }
@@ -8603,7 +8646,7 @@ var init_tool_server = __esm({
8603
8646
  case "file_read": {
8604
8647
  const readScope = callerId ? this.agentScopes.get(callerId) : void 0;
8605
8648
  if (readScope) {
8606
- const canonical = canonicalizeForBoundary((0, import_path5.resolve)(this.sandbox.projectRoot, args.path));
8649
+ const canonical = canonicalizeForBoundary((0, import_path6.resolve)(this.sandbox.projectRoot, args.path));
8607
8650
  if (!canonical.startsWith(readScope)) {
8608
8651
  throw new Error(`Read blocked: "${args.path}" is outside scope "${readScope}"`);
8609
8652
  }
@@ -8797,7 +8840,7 @@ ${truncateAtLine(fullDiff, 3e3)}`;
8797
8840
  }
8798
8841
  async requestPeerReview(callerId, diff, testResult) {
8799
8842
  const requestId = (0, import_crypto5.randomUUID)();
8800
- const reviewPromise = new Promise((resolve17, reject) => {
8843
+ const reviewPromise = new Promise((resolve18, reject) => {
8801
8844
  const timer = setTimeout(() => {
8802
8845
  this.pendingReviews.delete(requestId);
8803
8846
  reject(new Error("Review timed out"));
@@ -8806,7 +8849,7 @@ ${truncateAtLine(fullDiff, 3e3)}`;
8806
8849
  this.pendingReviews.set(requestId, {
8807
8850
  resolve: (r) => {
8808
8851
  clearTimeout(timer);
8809
- resolve17(r);
8852
+ resolve18(r);
8810
8853
  },
8811
8854
  reject: (e) => {
8812
8855
  clearTimeout(timer);
@@ -8832,7 +8875,7 @@ ${truncateAtLine(fullDiff, 3e3)}`;
8832
8875
  }
8833
8876
  async handleRunTests(args, callerId) {
8834
8877
  const { fileGlob } = args;
8835
- const resolvedGlob = (0, import_path5.resolve)(this.sandbox.projectRoot, fileGlob.replace(/\*/g, "_"));
8878
+ const resolvedGlob = (0, import_path6.resolve)(this.sandbox.projectRoot, fileGlob.replace(/\*/g, "_"));
8836
8879
  if (!resolvedGlob.startsWith(this.sandbox.projectRoot)) {
8837
8880
  throw new Error("run_tests: fileGlob must not contain path traversal");
8838
8881
  }
@@ -8840,7 +8883,7 @@ ${truncateAtLine(fullDiff, 3e3)}`;
8840
8883
  throw new Error("run_tests: fileGlob must not contain flags. Pass only file paths/globs.");
8841
8884
  }
8842
8885
  const scope = callerId ? this.agentScopes.get(callerId) : void 0;
8843
- const cwd = scope ? this.sandbox.validatePath((0, import_path5.resolve)(this.sandbox.projectRoot, scope)) : this.sandbox.projectRoot;
8886
+ const cwd = scope ? this.sandbox.validatePath((0, import_path6.resolve)(this.sandbox.projectRoot, scope)) : this.sandbox.projectRoot;
8844
8887
  let output;
8845
8888
  let success2;
8846
8889
  try {
@@ -8860,11 +8903,11 @@ ${truncateAtLine(fullDiff, 3e3)}`;
8860
8903
  }
8861
8904
  async handleRunTypecheck(callerId) {
8862
8905
  const scope = callerId ? this.agentScopes.get(callerId) : void 0;
8863
- const cwd = scope ? this.sandbox.validatePath((0, import_path5.resolve)(this.sandbox.projectRoot, scope)) : this.sandbox.projectRoot;
8906
+ const cwd = scope ? this.sandbox.validatePath((0, import_path6.resolve)(this.sandbox.projectRoot, scope)) : this.sandbox.projectRoot;
8864
8907
  let output;
8865
8908
  let success2;
8866
8909
  try {
8867
- const tsconfigPath = (0, import_path5.resolve)(this.sandbox.projectRoot, "tsconfig.json");
8910
+ const tsconfigPath = (0, import_path6.resolve)(this.sandbox.projectRoot, "tsconfig.json");
8868
8911
  output = await this.shellTools.shellExec({
8869
8912
  command: "npx",
8870
8913
  args: ["tsc", "--noEmit", "--project", tsconfigPath],
@@ -9179,6 +9222,17 @@ function loadSkills(agentId, skills, projectRoot, index, task) {
9179
9222
  for (const skill of effectiveSkills) {
9180
9223
  const content = resolveSkill(agentId, skill, projectRoot);
9181
9224
  if (!content) continue;
9225
+ const frontmatterStatus = parseSkillFrontmatter(content)?.status;
9226
+ if (frontmatterStatus === "failed" || frontmatterStatus === "silent_skill") {
9227
+ process.stderr.write(`[gossipcat] Skipping ${frontmatterStatus} skill ${agentId}/${skill} from injection
9228
+ `);
9229
+ dropped.push(skill);
9230
+ continue;
9231
+ }
9232
+ if (frontmatterStatus === "flagged_for_manual_review") {
9233
+ process.stderr.write(`[gossipcat] Injecting flagged_for_manual_review skill ${agentId}/${skill} \u2014 manual review recommended
9234
+ `);
9235
+ }
9182
9236
  const mode = index?.getSkillMode(agentId, skill) ?? "permanent";
9183
9237
  if (mode === "permanent") {
9184
9238
  permanent.push({ name: skill, content });
@@ -9247,16 +9301,16 @@ function resolveSkill(agentId, skill, projectRoot) {
9247
9301
  if (!normalized) return null;
9248
9302
  const filename = `${normalized}.md`;
9249
9303
  const bases = [
9250
- (0, import_path6.resolve)(projectRoot, ".gossip", "agents", agentId, "skills"),
9251
- (0, import_path6.resolve)(projectRoot, ".gossip", "skills"),
9252
- (0, import_path6.resolve)(__dirname, "default-skills")
9304
+ (0, import_path7.resolve)(projectRoot, ".gossip", "agents", agentId, "skills"),
9305
+ (0, import_path7.resolve)(projectRoot, ".gossip", "skills"),
9306
+ (0, import_path7.resolve)(__dirname, "default-skills")
9253
9307
  ];
9254
9308
  for (const base of bases) {
9255
- const candidate = (0, import_path6.resolve)(base, filename);
9256
- if (!candidate.startsWith(base + import_path6.sep)) continue;
9257
- if ((0, import_fs5.existsSync)(candidate)) {
9309
+ const candidate = (0, import_path7.resolve)(base, filename);
9310
+ if (!candidate.startsWith(base + import_path7.sep)) continue;
9311
+ if ((0, import_fs6.existsSync)(candidate)) {
9258
9312
  try {
9259
- return (0, import_fs5.readFileSync)(candidate, "utf-8");
9313
+ return (0, import_fs6.readFileSync)(candidate, "utf-8");
9260
9314
  } catch (err) {
9261
9315
  process.stderr.write(
9262
9316
  `[skill-loader] Failed to read skill file ${candidate}: ${err?.message ?? err}
@@ -9271,12 +9325,12 @@ function resolveSkill(agentId, skill, projectRoot) {
9271
9325
  function resolveSkillExists(agentId, skill, projectRoot) {
9272
9326
  return resolveSkill(agentId, skill, projectRoot) !== null;
9273
9327
  }
9274
- var import_fs5, import_path6, SAFE_AGENT_ID, MAX_CONTEXTUAL_SKILLS, MIN_KEYWORD_HITS, DEFAULT_KEYWORDS, patternCache, MAX_PATTERN_CACHE, MAX_KEYWORD_LENGTH;
9328
+ var import_fs6, import_path7, SAFE_AGENT_ID, MAX_CONTEXTUAL_SKILLS, MIN_KEYWORD_HITS, DEFAULT_KEYWORDS, patternCache, MAX_PATTERN_CACHE, MAX_KEYWORD_LENGTH;
9275
9329
  var init_skill_loader = __esm({
9276
9330
  "packages/orchestrator/src/skill-loader.ts"() {
9277
9331
  "use strict";
9278
- import_fs5 = require("fs");
9279
- import_path6 = require("path");
9332
+ import_fs6 = require("fs");
9333
+ import_path7 = require("path");
9280
9334
  init_skill_parser();
9281
9335
  init_skill_name();
9282
9336
  SAFE_AGENT_ID = /^[a-z0-9][a-z0-9_-]{0,62}$/;
@@ -9475,29 +9529,29 @@ Attributes can appear in any order. Do NOT include confirmations.`;
9475
9529
  });
9476
9530
 
9477
9531
  // packages/orchestrator/src/agent-memory.ts
9478
- var import_fs6, import_path7, AgentMemoryReader;
9532
+ var import_fs7, import_path8, AgentMemoryReader;
9479
9533
  var init_agent_memory = __esm({
9480
9534
  "packages/orchestrator/src/agent-memory.ts"() {
9481
9535
  "use strict";
9482
- import_fs6 = require("fs");
9483
- import_path7 = require("path");
9536
+ import_fs7 = require("fs");
9537
+ import_path8 = require("path");
9484
9538
  AgentMemoryReader = class {
9485
9539
  constructor(projectRoot) {
9486
9540
  this.projectRoot = projectRoot;
9487
9541
  }
9488
9542
  loadMemory(agentId, taskText) {
9489
- const memDir = (0, import_path7.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
9490
- const indexPath = (0, import_path7.join)(memDir, "MEMORY.md");
9491
- if (!(0, import_fs6.existsSync)(indexPath)) return null;
9543
+ const memDir = (0, import_path8.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
9544
+ const indexPath = (0, import_path8.join)(memDir, "MEMORY.md");
9545
+ if (!(0, import_fs7.existsSync)(indexPath)) return null;
9492
9546
  const parts = [];
9493
- const indexContent = (0, import_fs6.readFileSync)(indexPath, "utf-8");
9547
+ const indexContent = (0, import_fs7.readFileSync)(indexPath, "utf-8");
9494
9548
  const indexLines = indexContent.split("\n");
9495
9549
  parts.push(indexLines.length > 200 ? indexLines.slice(0, 200).join("\n") + "\n[Truncated]" : indexContent);
9496
- const knowledgeDir = (0, import_path7.join)(memDir, "knowledge");
9497
- if ((0, import_fs6.existsSync)(knowledgeDir)) {
9550
+ const knowledgeDir = (0, import_path8.join)(memDir, "knowledge");
9551
+ if ((0, import_fs7.existsSync)(knowledgeDir)) {
9498
9552
  const files = this.selectKnowledgeFiles(knowledgeDir, taskText);
9499
9553
  for (const file2 of files) {
9500
- let content = (0, import_fs6.readFileSync)(file2.path, "utf-8");
9554
+ let content = (0, import_fs7.readFileSync)(file2.path, "utf-8");
9501
9555
  content = content.replace(/<\/?(?:agent-memory|system|instructions)>/gi, "");
9502
9556
  parts.push(`<agent-memory>
9503
9557
  ${content}
@@ -9507,11 +9561,11 @@ ${content}
9507
9561
  }
9508
9562
  }
9509
9563
  }
9510
- const projectKnowledgeDir = (0, import_path7.join)(this.projectRoot, ".gossip", "agents", "_project", "memory", "knowledge");
9511
- if ((0, import_fs6.existsSync)(projectKnowledgeDir)) {
9564
+ const projectKnowledgeDir = (0, import_path8.join)(this.projectRoot, ".gossip", "agents", "_project", "memory", "knowledge");
9565
+ if ((0, import_fs7.existsSync)(projectKnowledgeDir)) {
9512
9566
  const projectFiles = this.selectKnowledgeFiles(projectKnowledgeDir, taskText, 3);
9513
9567
  for (const file2 of projectFiles) {
9514
- let content = (0, import_fs6.readFileSync)(file2.path, "utf-8");
9568
+ let content = (0, import_fs7.readFileSync)(file2.path, "utf-8");
9515
9569
  content = content.replace(/<\/?(?:agent-memory|system|instructions)>/gi, "");
9516
9570
  parts.push(`<project-context>
9517
9571
  ${content}
@@ -9521,19 +9575,19 @@ ${content}
9521
9575
  }
9522
9576
  }
9523
9577
  }
9524
- const calPath = (0, import_path7.join)(memDir, "calibration", "accuracy.md");
9525
- if ((0, import_fs6.existsSync)(calPath)) {
9526
- parts.push((0, import_fs6.readFileSync)(calPath, "utf-8"));
9578
+ const calPath = (0, import_path8.join)(memDir, "calibration", "accuracy.md");
9579
+ if ((0, import_fs7.existsSync)(calPath)) {
9580
+ parts.push((0, import_fs7.readFileSync)(calPath, "utf-8"));
9527
9581
  }
9528
9582
  return parts.join("\n\n");
9529
9583
  }
9530
9584
  selectKnowledgeFiles(knowledgeDir, taskText, maxFiles = 5) {
9531
- const files = (0, import_fs6.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
9585
+ const files = (0, import_fs7.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
9532
9586
  const scored = [];
9533
9587
  const lower = taskText.toLowerCase();
9534
9588
  for (const file2 of files) {
9535
- const filePath = (0, import_path7.join)(knowledgeDir, file2);
9536
- const content = (0, import_fs6.readFileSync)(filePath, "utf-8");
9589
+ const filePath = (0, import_path8.join)(knowledgeDir, file2);
9590
+ const content = (0, import_fs7.readFileSync)(filePath, "utf-8");
9537
9591
  const frontmatter = this.parseFrontmatter(content);
9538
9592
  if (frontmatter) {
9539
9593
  const warmth = this.calculateWarmth(frontmatter.importance, frontmatter.lastAccessed);
@@ -9547,7 +9601,7 @@ ${content}
9547
9601
  } else {
9548
9602
  const relevance = this.calculateRelevance(content.slice(0, 500), lower);
9549
9603
  try {
9550
- const mtime = (0, import_fs6.statSync)(filePath).mtimeMs;
9604
+ const mtime = (0, import_fs7.statSync)(filePath).mtimeMs;
9551
9605
  const ageDays = (Date.now() - mtime) / 864e5;
9552
9606
  const ageFactor = 1 / (1 + ageDays / 30);
9553
9607
  scored.push({ path: filePath, score: Math.max(relevance * ageFactor, 0.05) });
@@ -9602,7 +9656,7 @@ ${content}
9602
9656
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9603
9657
  const fmEnd = content.indexOf("\n---", 4);
9604
9658
  if (fmEnd < 0) {
9605
- (0, import_fs6.writeFileSync)(filePath, content);
9659
+ (0, import_fs7.writeFileSync)(filePath, content);
9606
9660
  return;
9607
9661
  }
9608
9662
  let frontmatter = content.slice(0, fmEnd);
@@ -9613,19 +9667,19 @@ ${content}
9613
9667
  const newCount = parseInt(countMatch[1]) + 1;
9614
9668
  frontmatter = frontmatter.replace(/accessCount:\s*\d+/, `accessCount: ${newCount}`);
9615
9669
  }
9616
- (0, import_fs6.writeFileSync)(filePath, frontmatter + body);
9670
+ (0, import_fs7.writeFileSync)(filePath, frontmatter + body);
9617
9671
  }
9618
9672
  };
9619
9673
  }
9620
9674
  });
9621
9675
 
9622
9676
  // packages/orchestrator/src/memory-compactor.ts
9623
- var import_fs7, import_path8, MAX_ARCHIVE_LINES, MemoryCompactor;
9677
+ var import_fs8, import_path9, MAX_ARCHIVE_LINES, MemoryCompactor;
9624
9678
  var init_memory_compactor = __esm({
9625
9679
  "packages/orchestrator/src/memory-compactor.ts"() {
9626
9680
  "use strict";
9627
- import_fs7 = require("fs");
9628
- import_path8 = require("path");
9681
+ import_fs8 = require("fs");
9682
+ import_path9 = require("path");
9629
9683
  MAX_ARCHIVE_LINES = 5e3;
9630
9684
  MemoryCompactor = class {
9631
9685
  constructor(projectRoot) {
@@ -9637,32 +9691,32 @@ var init_memory_compactor = __esm({
9637
9691
  return (importance ?? 0.5) * (1 / (1 + days / 30));
9638
9692
  }
9639
9693
  compactIfNeeded(agentId, maxEntries = 20) {
9640
- const memDir = (0, import_path8.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
9641
- const tasksPath = (0, import_path8.join)(memDir, "tasks.jsonl");
9642
- const lockPath = (0, import_path8.join)(memDir, "tasks.jsonl.lock");
9643
- if (!(0, import_fs7.existsSync)(tasksPath)) return { archived: 0 };
9644
- if ((0, import_fs7.existsSync)(lockPath)) {
9694
+ const memDir = (0, import_path9.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
9695
+ const tasksPath = (0, import_path9.join)(memDir, "tasks.jsonl");
9696
+ const lockPath = (0, import_path9.join)(memDir, "tasks.jsonl.lock");
9697
+ if (!(0, import_fs8.existsSync)(tasksPath)) return { archived: 0 };
9698
+ if ((0, import_fs8.existsSync)(lockPath)) {
9645
9699
  try {
9646
- const lockTs = parseInt((0, import_fs7.readFileSync)(lockPath, "utf-8"), 10);
9700
+ const lockTs = parseInt((0, import_fs8.readFileSync)(lockPath, "utf-8"), 10);
9647
9701
  if (!Number.isNaN(lockTs) && Date.now() - lockTs < 6e4) return { archived: 0 };
9648
- (0, import_fs7.unlinkSync)(lockPath);
9702
+ (0, import_fs8.unlinkSync)(lockPath);
9649
9703
  } catch {
9650
9704
  try {
9651
- (0, import_fs7.unlinkSync)(lockPath);
9705
+ (0, import_fs8.unlinkSync)(lockPath);
9652
9706
  } catch {
9653
9707
  return { archived: 0 };
9654
9708
  }
9655
9709
  }
9656
9710
  }
9657
9711
  try {
9658
- const fd = (0, import_fs7.openSync)(lockPath, import_fs7.constants.O_WRONLY | import_fs7.constants.O_CREAT | import_fs7.constants.O_EXCL);
9659
- (0, import_fs7.writeFileSync)(fd, `${Date.now()}`);
9660
- (0, import_fs7.closeSync)(fd);
9712
+ const fd = (0, import_fs8.openSync)(lockPath, import_fs8.constants.O_WRONLY | import_fs8.constants.O_CREAT | import_fs8.constants.O_EXCL);
9713
+ (0, import_fs8.writeFileSync)(fd, `${Date.now()}`);
9714
+ (0, import_fs8.closeSync)(fd);
9661
9715
  } catch {
9662
9716
  return { archived: 0 };
9663
9717
  }
9664
9718
  try {
9665
- const lines = (0, import_fs7.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
9719
+ const lines = (0, import_fs8.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
9666
9720
  if (lines.length <= maxEntries) return { archived: 0 };
9667
9721
  const entries = [];
9668
9722
  let dropped = 0;
@@ -9679,7 +9733,7 @@ var init_memory_compactor = __esm({
9679
9733
  const toArchive = entries.slice(0, entries.length - maxEntries);
9680
9734
  const toKeep = entries.slice(entries.length - maxEntries);
9681
9735
  toKeep.sort((a, b) => a.idx - b.idx);
9682
- const archivePath = (0, import_path8.join)(memDir, "archive.jsonl");
9736
+ const archivePath = (0, import_path9.join)(memDir, "archive.jsonl");
9683
9737
  for (const item of toArchive) {
9684
9738
  const archived = {
9685
9739
  archivedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -9687,22 +9741,22 @@ var init_memory_compactor = __esm({
9687
9741
  warmth: item.warmth,
9688
9742
  entry: item.entry
9689
9743
  };
9690
- (0, import_fs7.appendFileSync)(archivePath, JSON.stringify(archived) + "\n");
9744
+ (0, import_fs8.appendFileSync)(archivePath, JSON.stringify(archived) + "\n");
9691
9745
  }
9692
9746
  try {
9693
- if ((0, import_fs7.existsSync)(archivePath)) {
9694
- const archiveLines = (0, import_fs7.readFileSync)(archivePath, "utf-8").trim().split("\n");
9747
+ if ((0, import_fs8.existsSync)(archivePath)) {
9748
+ const archiveLines = (0, import_fs8.readFileSync)(archivePath, "utf-8").trim().split("\n");
9695
9749
  if (archiveLines.length > MAX_ARCHIVE_LINES) {
9696
- (0, import_fs7.writeFileSync)(archivePath, archiveLines.slice(-MAX_ARCHIVE_LINES).join("\n") + "\n");
9750
+ (0, import_fs8.writeFileSync)(archivePath, archiveLines.slice(-MAX_ARCHIVE_LINES).join("\n") + "\n");
9697
9751
  }
9698
9752
  }
9699
9753
  } catch {
9700
9754
  }
9701
- (0, import_fs7.writeFileSync)(tasksPath, toKeep.map((e) => e.line).join("\n") + "\n");
9755
+ (0, import_fs8.writeFileSync)(tasksPath, toKeep.map((e) => e.line).join("\n") + "\n");
9702
9756
  return { archived: toArchive.length, dropped: dropped || void 0, message: `Compacted ${toArchive.length} memories for ${agentId}${dropped ? ` (${dropped} malformed lines dropped)` : ""}` };
9703
9757
  } finally {
9704
9758
  try {
9705
- (0, import_fs7.unlinkSync)(lockPath);
9759
+ (0, import_fs8.unlinkSync)(lockPath);
9706
9760
  } catch {
9707
9761
  }
9708
9762
  }
@@ -9721,11 +9775,11 @@ function sanitizeName(name) {
9721
9775
  function discoverProjectStructure(projectRoot) {
9722
9776
  try {
9723
9777
  const parts = [];
9724
- const entries = (0, import_fs8.readdirSync)(projectRoot, { withFileTypes: true });
9778
+ const entries = (0, import_fs9.readdirSync)(projectRoot, { withFileTypes: true });
9725
9779
  for (const entry of entries) {
9726
9780
  if (!entry.isDirectory() || isSkipped(entry.name)) continue;
9727
9781
  try {
9728
- const allChildren = (0, import_fs8.readdirSync)((0, import_path9.join)(projectRoot, entry.name)).filter((f) => !f.startsWith("."));
9782
+ const allChildren = (0, import_fs9.readdirSync)((0, import_path10.join)(projectRoot, entry.name)).filter((f) => !f.startsWith("."));
9729
9783
  if (allChildren.length === 0) continue;
9730
9784
  const shown = allChildren.slice(0, MAX_CHILDREN).map(sanitizeName);
9731
9785
  const suffix = allChildren.length > MAX_CHILDREN ? `, ...${allChildren.length - MAX_CHILDREN} more` : "";
@@ -9738,12 +9792,12 @@ function discoverProjectStructure(projectRoot) {
9738
9792
  return [];
9739
9793
  }
9740
9794
  }
9741
- var import_fs8, import_path9, SKIP, MAX_CHILDREN;
9795
+ var import_fs9, import_path10, SKIP, MAX_CHILDREN;
9742
9796
  var init_project_structure = __esm({
9743
9797
  "packages/orchestrator/src/project-structure.ts"() {
9744
9798
  "use strict";
9745
- import_fs8 = require("fs");
9746
- import_path9 = require("path");
9799
+ import_fs9 = require("fs");
9800
+ import_path10 = require("path");
9747
9801
  SKIP = /* @__PURE__ */ new Set([
9748
9802
  "node_modules",
9749
9803
  "build",
@@ -9784,12 +9838,12 @@ function truncateStartAndEnd(text, maxLen) {
9784
9838
 
9785
9839
  ${text.slice(-tail)}`;
9786
9840
  }
9787
- var import_fs9, import_path10, MemoryWriter;
9841
+ var import_fs10, import_path11, MemoryWriter;
9788
9842
  var init_memory_writer = __esm({
9789
9843
  "packages/orchestrator/src/memory-writer.ts"() {
9790
9844
  "use strict";
9791
- import_fs9 = require("fs");
9792
- import_path10 = require("path");
9845
+ import_fs10 = require("fs");
9846
+ import_path11 = require("path");
9793
9847
  init_memory_compactor();
9794
9848
  init_project_structure();
9795
9849
  MemoryWriter = class {
@@ -9802,12 +9856,12 @@ var init_memory_writer = __esm({
9802
9856
  this.summaryLlm = llm;
9803
9857
  }
9804
9858
  getMemDir(agentId) {
9805
- return (0, import_path10.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
9859
+ return (0, import_path11.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
9806
9860
  }
9807
9861
  ensureDirs(agentId) {
9808
9862
  const memDir = this.getMemDir(agentId);
9809
- (0, import_fs9.mkdirSync)((0, import_path10.join)(memDir, "knowledge"), { recursive: true });
9810
- (0, import_fs9.mkdirSync)((0, import_path10.join)(memDir, "calibration"), { recursive: true });
9863
+ (0, import_fs10.mkdirSync)((0, import_path11.join)(memDir, "knowledge"), { recursive: true });
9864
+ (0, import_fs10.mkdirSync)((0, import_path11.join)(memDir, "calibration"), { recursive: true });
9811
9865
  return memDir;
9812
9866
  }
9813
9867
  async writeTaskEntry(agentId, data) {
@@ -9825,7 +9879,7 @@ var init_memory_writer = __esm({
9825
9879
  importance: this.deriveImportance(data.scores),
9826
9880
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
9827
9881
  };
9828
- (0, import_fs9.appendFileSync)((0, import_path10.join)(memDir, "tasks.jsonl"), JSON.stringify(entry) + "\n");
9882
+ (0, import_fs10.appendFileSync)((0, import_path11.join)(memDir, "tasks.jsonl"), JSON.stringify(entry) + "\n");
9829
9883
  }
9830
9884
  /**
9831
9885
  * Extract key facts from a task result and write as a knowledge entry.
@@ -9834,7 +9888,7 @@ var init_memory_writer = __esm({
9834
9888
  */
9835
9889
  async writeKnowledgeFromResult(agentId, data) {
9836
9890
  const memDir = this.ensureDirs(agentId);
9837
- const knowledgeDir = (0, import_path10.join)(memDir, "knowledge");
9891
+ const knowledgeDir = (0, import_path11.join)(memDir, "knowledge");
9838
9892
  const safeResult = data.result.length > 5e4 ? data.result.slice(0, 5e4) : data.result;
9839
9893
  let cognitiveSummary = null;
9840
9894
  if (this.summaryLlm) {
@@ -9884,7 +9938,7 @@ ${cleanSummary}` : cleanSummary;
9884
9938
  ...data.agentAccuracy !== void 0 && data.agentAccuracy < 0.4 ? ["> \u26A0 This agent has low accuracy (" + (data.agentAccuracy * 100).toFixed(0) + "%). Treat factual claims as unverified.\n"] : [],
9885
9939
  body
9886
9940
  ].join("\n");
9887
- (0, import_fs9.writeFileSync)((0, import_path10.join)(knowledgeDir, filename), content);
9941
+ (0, import_fs10.writeFileSync)((0, import_path11.join)(knowledgeDir, filename), content);
9888
9942
  }
9889
9943
  /**
9890
9944
  * Generate a cognitive summary — what the agent learned, not just what it saw.
@@ -9925,7 +9979,7 @@ ${truncateStartAndEnd(result, 4e3)}`
9925
9979
  /** Session summary data shared across public methods */
9926
9980
  sessionSummaryData(data) {
9927
9981
  const memDir = this.ensureDirs("_project");
9928
- const knowledgeDir = (0, import_path10.join)(memDir, "knowledge");
9982
+ const knowledgeDir = (0, import_path11.join)(memDir, "knowledge");
9929
9983
  const now = /* @__PURE__ */ new Date();
9930
9984
  const timestamp = now.toISOString().replace(/[:.]/g, "-").slice(0, 19);
9931
9985
  const today = now.toISOString().split("T")[0];
@@ -9948,9 +10002,9 @@ ${discovered.join("\n")}
9948
10002
  let existingMemoriesContext = "";
9949
10003
  const existingFiles = [];
9950
10004
  try {
9951
- const files = (0, import_fs9.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md"));
10005
+ const files = (0, import_fs10.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md"));
9952
10006
  for (const f of files) {
9953
- const content = (0, import_fs9.readFileSync)((0, import_path10.join)(knowledgeDir, f), "utf-8");
10007
+ const content = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir, f), "utf-8");
9954
10008
  const descMatch = content.match(/description:\s*(.+)/);
9955
10009
  const desc = descMatch ? descMatch[1].trim() : "(no description)";
9956
10010
  existingFiles.push(f);
@@ -10060,10 +10114,18 @@ ${rawInput.slice(0, SESSION_SUMMARY_MAX_CHARS)}`;
10060
10114
  summaryBody = raw;
10061
10115
  } else {
10062
10116
  rawLlmResponse = raw;
10063
- const hasSummaryLine = /^SUMMARY:\s*.+/m.test(raw);
10064
10117
  const hasSectionHeader = /^##\s+\w/m.test(raw);
10065
- if (!hasSummaryLine || !hasSectionHeader) {
10118
+ if (!hasSectionHeader) {
10066
10119
  process.stderr.write("[gossipcat] Session summary missing required structure, using raw fallback\n");
10120
+ try {
10121
+ const debugPath = (0, import_path11.join)(memDir, "last-malformed-summary.txt");
10122
+ (0, import_fs10.writeFileSync)(debugPath, `# Malformed session summary @ ${timestamp}
10123
+ # No ## header found in LLM output.
10124
+
10125
+ ---
10126
+ ${raw}`);
10127
+ } catch {
10128
+ }
10067
10129
  summaryBody = `> \u26A0\uFE0F LLM summary malformed \u2014 raw data below.
10068
10130
 
10069
10131
  ${rawInput.slice(0, SESSION_SUMMARY_MAX_CHARS)}`;
@@ -10105,15 +10167,15 @@ ${rawInput.slice(0, SESSION_SUMMARY_MAX_CHARS)}`;
10105
10167
  if (staleFiles.length > 0) {
10106
10168
  for (const sf of staleFiles) {
10107
10169
  try {
10108
- const filePath = (0, import_path10.join)(knowledgeDir, sf);
10109
- let fileContent = (0, import_fs9.readFileSync)(filePath, "utf-8");
10170
+ const filePath = (0, import_path11.join)(knowledgeDir, sf);
10171
+ let fileContent = (0, import_fs10.readFileSync)(filePath, "utf-8");
10110
10172
  fileContent = fileContent.replace(/importance:\s*[\d.]+/, "importance: 0.1");
10111
10173
  if (!/status:/.test(fileContent)) {
10112
10174
  fileContent = fileContent.replace(/\n---/, "\nstatus: shipped\n---");
10113
10175
  } else {
10114
10176
  fileContent = fileContent.replace(/status:\s*.+/, "status: shipped");
10115
10177
  }
10116
- (0, import_fs9.writeFileSync)(filePath, fileContent);
10178
+ (0, import_fs10.writeFileSync)(filePath, fileContent);
10117
10179
  process.stderr.write(`[gossipcat] \u{1F5DC}\uFE0F Marked stale: ${sf}
10118
10180
  `);
10119
10181
  } catch {
@@ -10134,8 +10196,8 @@ ${rawInput.slice(0, SESSION_SUMMARY_MAX_CHARS)}`;
10134
10196
  "",
10135
10197
  summaryBody
10136
10198
  ].filter((l) => l !== "").join("\n");
10137
- (0, import_fs9.writeFileSync)((0, import_path10.join)(knowledgeDir, filename), content);
10138
- const nextSessionPath = (0, import_path10.join)(this.projectRoot, ".gossip", "next-session.md");
10199
+ (0, import_fs10.writeFileSync)((0, import_path11.join)(knowledgeDir, filename), content);
10200
+ const nextSessionPath = (0, import_path11.join)(this.projectRoot, ".gossip", "next-session.md");
10139
10201
  const openMatch = summaryBody.match(/##\s+Open[^\n]*\n([\s\S]*?)(?=\n##|\s*$)/i);
10140
10202
  const NEXT_SESSION_MAX_CHARS = 1500;
10141
10203
  const nextSessionContent = openMatch ? `# Next Session
@@ -10145,11 +10207,11 @@ ${openMatch[0].trim()}
10145
10207
 
10146
10208
  ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
10147
10209
  `;
10148
- (0, import_fs9.writeFileSync)(nextSessionPath, nextSessionContent);
10149
- const migrationTasksPath = (0, import_path10.join)(memDir, "tasks.jsonl");
10150
- if ((0, import_fs9.existsSync)(migrationTasksPath)) {
10210
+ (0, import_fs10.writeFileSync)(nextSessionPath, nextSessionContent);
10211
+ const migrationTasksPath = (0, import_path11.join)(memDir, "tasks.jsonl");
10212
+ if ((0, import_fs10.existsSync)(migrationTasksPath)) {
10151
10213
  try {
10152
- const mLines = (0, import_fs9.readFileSync)(migrationTasksPath, "utf-8").trim().split("\n").filter(Boolean);
10214
+ const mLines = (0, import_fs10.readFileSync)(migrationTasksPath, "utf-8").trim().split("\n").filter(Boolean);
10153
10215
  let migrated = false;
10154
10216
  const fixed = mLines.map((line) => {
10155
10217
  try {
@@ -10163,7 +10225,7 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
10163
10225
  return line;
10164
10226
  }
10165
10227
  });
10166
- if (migrated) (0, import_fs9.writeFileSync)(migrationTasksPath, fixed.join("\n") + "\n");
10228
+ if (migrated) (0, import_fs10.writeFileSync)(migrationTasksPath, fixed.join("\n") + "\n");
10167
10229
  } catch {
10168
10230
  }
10169
10231
  }
@@ -10188,10 +10250,10 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
10188
10250
  /** Shared warmth-aware pruning — evicts lowest-warmth files, respects pinned */
10189
10251
  pruneKnowledgeDir(knowledgeDir, maxFiles) {
10190
10252
  try {
10191
- const existing = (0, import_fs9.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md")).sort();
10253
+ const existing = (0, import_fs10.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md")).sort();
10192
10254
  if (existing.length >= maxFiles) {
10193
10255
  const scored = existing.map((f) => {
10194
- const content = (0, import_fs9.readFileSync)((0, import_path10.join)(knowledgeDir, f), "utf-8");
10256
+ const content = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir, f), "utf-8");
10195
10257
  const importance = parseFloat(content.match(/importance:\s*([\d.]+)/)?.[1] ?? "0.5");
10196
10258
  const isPinned = /pinned:\s*true/i.test(content);
10197
10259
  const ts = f.slice(0, 19).replace(/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})/, "$1T$2:$3:$4");
@@ -10210,10 +10272,10 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
10210
10272
  );
10211
10273
  }
10212
10274
  for (const item of toEvict) {
10213
- (0, import_fs9.unlinkSync)((0, import_path10.join)(knowledgeDir, item.file));
10275
+ (0, import_fs10.unlinkSync)((0, import_path11.join)(knowledgeDir, item.file));
10214
10276
  }
10215
10277
  }
10216
- const sessionFiles = (0, import_fs9.readdirSync)(knowledgeDir).filter((f) => f.endsWith("-session.md")).sort();
10278
+ const sessionFiles = (0, import_fs10.readdirSync)(knowledgeDir).filter((f) => f.endsWith("-session.md")).sort();
10217
10279
  const MAX_SESSION_FILES = 5;
10218
10280
  const SESSION_TTL_DAYS = 14;
10219
10281
  for (const sf of sessionFiles) {
@@ -10221,10 +10283,10 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
10221
10283
  const ts = sf.slice(0, 19).replace(/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})/, "$1T$2:$3:$4");
10222
10284
  const days = (Date.now() - new Date(ts).getTime()) / 864e5;
10223
10285
  if (days > SESSION_TTL_DAYS) {
10224
- const sfPath = (0, import_path10.join)(knowledgeDir, sf);
10225
- const sfContent = (0, import_fs9.readFileSync)(sfPath, "utf-8");
10286
+ const sfPath = (0, import_path11.join)(knowledgeDir, sf);
10287
+ const sfContent = (0, import_fs10.readFileSync)(sfPath, "utf-8");
10226
10288
  if (!/importance:\s*0\.1/.test(sfContent)) {
10227
- (0, import_fs9.writeFileSync)(sfPath, sfContent.replace(/importance:\s*[\d.]+/, "importance: 0.1"));
10289
+ (0, import_fs10.writeFileSync)(sfPath, sfContent.replace(/importance:\s*[\d.]+/, "importance: 0.1"));
10228
10290
  }
10229
10291
  }
10230
10292
  } catch {
@@ -10235,7 +10297,7 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
10235
10297
  const bodies = [];
10236
10298
  for (const sf of toCompact) {
10237
10299
  try {
10238
- const sfContent = (0, import_fs9.readFileSync)((0, import_path10.join)(knowledgeDir, sf), "utf-8");
10300
+ const sfContent = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir, sf), "utf-8");
10239
10301
  const body = sfContent.split("---").slice(2).join("---").trim();
10240
10302
  if (body) bodies.push(body.slice(0, 800));
10241
10303
  } catch {
@@ -10255,10 +10317,10 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
10255
10317
  "",
10256
10318
  ...bodies
10257
10319
  ].join("\n").slice(0, 3e3);
10258
- (0, import_fs9.writeFileSync)((0, import_path10.join)(knowledgeDir, digestName), digestContent);
10320
+ (0, import_fs10.writeFileSync)((0, import_path11.join)(knowledgeDir, digestName), digestContent);
10259
10321
  for (const sf of toCompact) {
10260
10322
  try {
10261
- (0, import_fs9.unlinkSync)((0, import_path10.join)(knowledgeDir, sf));
10323
+ (0, import_fs10.unlinkSync)((0, import_path11.join)(knowledgeDir, sf));
10262
10324
  } catch {
10263
10325
  }
10264
10326
  }
@@ -10441,7 +10503,7 @@ ${result}`;
10441
10503
  writeConsensusKnowledge(agentId, findings) {
10442
10504
  if (findings.length === 0) return;
10443
10505
  const memDir = this.ensureDirs(agentId);
10444
- const knowledgeDir = (0, import_path10.join)(memDir, "knowledge");
10506
+ const knowledgeDir = (0, import_path11.join)(memDir, "knowledge");
10445
10507
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
10446
10508
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
10447
10509
  const filename = `${timestamp}-consensus.md`;
@@ -10471,7 +10533,7 @@ ${result}`;
10471
10533
  })
10472
10534
  ].join("\n");
10473
10535
  this.pruneKnowledgeDir(knowledgeDir, 25);
10474
- (0, import_fs9.writeFileSync)((0, import_path10.join)(knowledgeDir, filename), content);
10536
+ (0, import_fs10.writeFileSync)((0, import_path11.join)(knowledgeDir, filename), content);
10475
10537
  }
10476
10538
  /**
10477
10539
  * Update task memory importance based on consensus signals.
@@ -10494,14 +10556,14 @@ ${result}`;
10494
10556
  taskAdj.set(s.taskId, (taskAdj.get(s.taskId) ?? 0) + weight);
10495
10557
  }
10496
10558
  for (const [agentId, taskAdjustments] of adjustments) {
10497
- const memDir = (0, import_path10.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
10498
- const tasksPath = (0, import_path10.join)(memDir, "tasks.jsonl");
10499
- const lockPath = (0, import_path10.join)(memDir, "tasks.jsonl.lock");
10500
- if (!(0, import_fs9.existsSync)(tasksPath)) continue;
10501
- if ((0, import_fs9.existsSync)(lockPath)) continue;
10559
+ const memDir = (0, import_path11.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
10560
+ const tasksPath = (0, import_path11.join)(memDir, "tasks.jsonl");
10561
+ const lockPath = (0, import_path11.join)(memDir, "tasks.jsonl.lock");
10562
+ if (!(0, import_fs10.existsSync)(tasksPath)) continue;
10563
+ if ((0, import_fs10.existsSync)(lockPath)) continue;
10502
10564
  try {
10503
- (0, import_fs9.writeFileSync)(lockPath, `${Date.now()}`);
10504
- const lines = (0, import_fs9.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
10565
+ (0, import_fs10.writeFileSync)(lockPath, `${Date.now()}`);
10566
+ const lines = (0, import_fs10.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
10505
10567
  let modified = false;
10506
10568
  const updated = lines.map((line) => {
10507
10569
  try {
@@ -10518,12 +10580,12 @@ ${result}`;
10518
10580
  }
10519
10581
  });
10520
10582
  if (modified) {
10521
- (0, import_fs9.writeFileSync)(tasksPath, updated.join("\n") + "\n");
10583
+ (0, import_fs10.writeFileSync)(tasksPath, updated.join("\n") + "\n");
10522
10584
  }
10523
10585
  } catch {
10524
10586
  } finally {
10525
10587
  try {
10526
- (0, import_fs9.unlinkSync)(lockPath);
10588
+ (0, import_fs10.unlinkSync)(lockPath);
10527
10589
  } catch {
10528
10590
  }
10529
10591
  }
@@ -10533,13 +10595,13 @@ ${result}`;
10533
10595
  const memDir = this.getMemDir(agentId);
10534
10596
  const parts = [`# Agent Memory \u2014 ${agentId}
10535
10597
  `];
10536
- const knowledgeDir = (0, import_path10.join)(memDir, "knowledge");
10537
- if ((0, import_fs9.existsSync)(knowledgeDir)) {
10538
- const files = (0, import_fs9.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md")).sort().reverse();
10598
+ const knowledgeDir = (0, import_path11.join)(memDir, "knowledge");
10599
+ if ((0, import_fs10.existsSync)(knowledgeDir)) {
10600
+ const files = (0, import_fs10.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md")).sort().reverse();
10539
10601
  if (files.length > 0) {
10540
10602
  parts.push("## Knowledge (most recent first)");
10541
10603
  for (const file2 of files) {
10542
- const content = (0, import_fs9.readFileSync)((0, import_path10.join)(knowledgeDir, file2), "utf-8");
10604
+ const content = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir, file2), "utf-8");
10543
10605
  const descMatch = content.match(/description:\s*(.+)/);
10544
10606
  const desc = descMatch ? descMatch[1].trim() : file2.replace(".md", "");
10545
10607
  parts.push(`- [${file2.replace(".md", "")}](knowledge/${file2}) \u2014 ${desc}`);
@@ -10547,17 +10609,17 @@ ${result}`;
10547
10609
  parts.push("");
10548
10610
  }
10549
10611
  }
10550
- const calPath = (0, import_path10.join)(memDir, "calibration", "accuracy.md");
10551
- if ((0, import_fs9.existsSync)(calPath)) {
10552
- const content = (0, import_fs9.readFileSync)(calPath, "utf-8");
10612
+ const calPath = (0, import_path11.join)(memDir, "calibration", "accuracy.md");
10613
+ if ((0, import_fs10.existsSync)(calPath)) {
10614
+ const content = (0, import_fs10.readFileSync)(calPath, "utf-8");
10553
10615
  const descMatch = content.match(/description:\s*(.+)/);
10554
10616
  parts.push("## Calibration");
10555
10617
  parts.push(`- [accuracy](calibration/accuracy.md) \u2014 ${descMatch ? descMatch[1].trim() : "accuracy data"}`);
10556
10618
  parts.push("");
10557
10619
  }
10558
- const tasksPath = (0, import_path10.join)(memDir, "tasks.jsonl");
10559
- if ((0, import_fs9.existsSync)(tasksPath)) {
10560
- const lines = (0, import_fs9.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
10620
+ const tasksPath = (0, import_path11.join)(memDir, "tasks.jsonl");
10621
+ if ((0, import_fs10.existsSync)(tasksPath)) {
10622
+ const lines = (0, import_fs10.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
10561
10623
  const recent = lines.slice(-5).reverse();
10562
10624
  if (recent.length > 0) {
10563
10625
  parts.push("## Recent Tasks");
@@ -10574,12 +10636,12 @@ ${result}`;
10574
10636
  }
10575
10637
  }
10576
10638
  try {
10577
- const knowledgeDir2 = (0, import_path10.join)(memDir, "knowledge");
10578
- if ((0, import_fs9.existsSync)(knowledgeDir2)) {
10579
- const knowledgeFiles = (0, import_fs9.readdirSync)(knowledgeDir2).filter((f) => f.endsWith(".md")).sort().reverse().slice(0, 5);
10639
+ const knowledgeDir2 = (0, import_path11.join)(memDir, "knowledge");
10640
+ if ((0, import_fs10.existsSync)(knowledgeDir2)) {
10641
+ const knowledgeFiles = (0, import_fs10.readdirSync)(knowledgeDir2).filter((f) => f.endsWith(".md")).sort().reverse().slice(0, 5);
10580
10642
  const patterns = [];
10581
10643
  for (const kf of knowledgeFiles) {
10582
- const kContent = (0, import_fs9.readFileSync)((0, import_path10.join)(knowledgeDir2, kf), "utf-8");
10644
+ const kContent = (0, import_fs10.readFileSync)((0, import_path11.join)(knowledgeDir2, kf), "utf-8");
10583
10645
  const decisionsMatch = kContent.match(/Decisions: (.+)/);
10584
10646
  if (decisionsMatch) patterns.push(decisionsMatch[1].trim());
10585
10647
  const failuresMatch = kContent.match(/Failures: (.+)/);
@@ -10591,19 +10653,19 @@ ${result}`;
10591
10653
  }
10592
10654
  } catch {
10593
10655
  }
10594
- (0, import_fs9.writeFileSync)((0, import_path10.join)(memDir, "MEMORY.md"), parts.join("\n"));
10656
+ (0, import_fs10.writeFileSync)((0, import_path11.join)(memDir, "MEMORY.md"), parts.join("\n"));
10595
10657
  }
10596
10658
  };
10597
10659
  }
10598
10660
  });
10599
10661
 
10600
10662
  // packages/orchestrator/src/task-graph.ts
10601
- var import_fs10, import_path11, MAX_SCAN_LINES, TaskGraph;
10663
+ var import_fs11, import_path12, MAX_SCAN_LINES, TaskGraph;
10602
10664
  var init_task_graph = __esm({
10603
10665
  "packages/orchestrator/src/task-graph.ts"() {
10604
10666
  "use strict";
10605
- import_fs10 = require("fs");
10606
- import_path11 = require("path");
10667
+ import_fs11 = require("fs");
10668
+ import_path12 = require("path");
10607
10669
  MAX_SCAN_LINES = 1e3;
10608
10670
  TaskGraph = class {
10609
10671
  graphPath;
@@ -10613,14 +10675,14 @@ var init_task_graph = __esm({
10613
10675
  // taskId → last event line number
10614
10676
  eventCount = 0;
10615
10677
  constructor(projectRoot) {
10616
- const gossipDir2 = (0, import_path11.join)(projectRoot, ".gossip");
10617
- (0, import_fs10.mkdirSync)(gossipDir2, { recursive: true });
10618
- this.graphPath = (0, import_path11.join)(gossipDir2, "task-graph.jsonl");
10619
- this.syncMetaPath = (0, import_path11.join)(gossipDir2, "task-graph-sync.json");
10620
- this.indexPath = (0, import_path11.join)(gossipDir2, "task-graph-index.json");
10678
+ const gossipDir2 = (0, import_path12.join)(projectRoot, ".gossip");
10679
+ (0, import_fs11.mkdirSync)(gossipDir2, { recursive: true });
10680
+ this.graphPath = (0, import_path12.join)(gossipDir2, "task-graph.jsonl");
10681
+ this.syncMetaPath = (0, import_path12.join)(gossipDir2, "task-graph-sync.json");
10682
+ this.indexPath = (0, import_path12.join)(gossipDir2, "task-graph-index.json");
10621
10683
  this.loadIndex();
10622
- if ((0, import_fs10.existsSync)(this.graphPath)) {
10623
- const buf = (0, import_fs10.readFileSync)(this.graphPath);
10684
+ if ((0, import_fs11.existsSync)(this.graphPath)) {
10685
+ const buf = (0, import_fs11.readFileSync)(this.graphPath);
10624
10686
  let count = 0;
10625
10687
  for (let i = 0; i < buf.length; i++) {
10626
10688
  if (buf[i] === 10) count++;
@@ -10629,9 +10691,9 @@ var init_task_graph = __esm({
10629
10691
  }
10630
10692
  }
10631
10693
  loadIndex() {
10632
- if ((0, import_fs10.existsSync)(this.indexPath)) {
10694
+ if ((0, import_fs11.existsSync)(this.indexPath)) {
10633
10695
  try {
10634
- const data = JSON.parse((0, import_fs10.readFileSync)(this.indexPath, "utf-8"));
10696
+ const data = JSON.parse((0, import_fs11.readFileSync)(this.indexPath, "utf-8"));
10635
10697
  this.index = new Map(Object.entries(data).map(([k, v]) => [k, Number(v)]));
10636
10698
  } catch {
10637
10699
  }
@@ -10639,10 +10701,10 @@ var init_task_graph = __esm({
10639
10701
  }
10640
10702
  /** Save index to disk (call explicitly, not on every append) */
10641
10703
  flushIndex() {
10642
- (0, import_fs10.writeFileSync)(this.indexPath, JSON.stringify(Object.fromEntries(this.index)));
10704
+ (0, import_fs11.writeFileSync)(this.indexPath, JSON.stringify(Object.fromEntries(this.index)));
10643
10705
  }
10644
10706
  appendEvent(event) {
10645
- (0, import_fs10.appendFileSync)(this.graphPath, JSON.stringify(event) + "\n");
10707
+ (0, import_fs11.appendFileSync)(this.graphPath, JSON.stringify(event) + "\n");
10646
10708
  if ("taskId" in event) {
10647
10709
  this.index.set(event.taskId, this.eventCount);
10648
10710
  }
@@ -10724,8 +10786,8 @@ var init_task_graph = __esm({
10724
10786
  }
10725
10787
  // ── Read methods ─────────────────────────────────────────────────────
10726
10788
  readEvents() {
10727
- if (!(0, import_fs10.existsSync)(this.graphPath)) return [];
10728
- const content = (0, import_fs10.readFileSync)(this.graphPath, "utf-8");
10789
+ if (!(0, import_fs11.existsSync)(this.graphPath)) return [];
10790
+ const content = (0, import_fs11.readFileSync)(this.graphPath, "utf-8");
10729
10791
  const lines = content.trim().split("\n").filter(Boolean);
10730
10792
  const tail = lines.slice(-MAX_SCAN_LINES);
10731
10793
  return tail.map((line) => {
@@ -10807,14 +10869,14 @@ var init_task_graph = __esm({
10807
10869
  return this.eventCount;
10808
10870
  }
10809
10871
  getSyncMeta() {
10810
- if (!(0, import_fs10.existsSync)(this.syncMetaPath)) {
10872
+ if (!(0, import_fs11.existsSync)(this.syncMetaPath)) {
10811
10873
  return { lastSync: "", lastSyncEventCount: 0 };
10812
10874
  }
10813
- return JSON.parse((0, import_fs10.readFileSync)(this.syncMetaPath, "utf-8"));
10875
+ return JSON.parse((0, import_fs11.readFileSync)(this.syncMetaPath, "utf-8"));
10814
10876
  }
10815
10877
  updateSyncMeta(meta3) {
10816
10878
  const current = this.getSyncMeta();
10817
- (0, import_fs10.writeFileSync)(this.syncMetaPath, JSON.stringify({ ...current, ...meta3 }, null, 2));
10879
+ (0, import_fs11.writeFileSync)(this.syncMetaPath, JSON.stringify({ ...current, ...meta3 }, null, 2));
10818
10880
  }
10819
10881
  getUnsynced(lastSyncTimestamp) {
10820
10882
  if (!lastSyncTimestamp) return this.readEvents();
@@ -10825,12 +10887,12 @@ var init_task_graph = __esm({
10825
10887
  });
10826
10888
 
10827
10889
  // packages/orchestrator/src/skill-catalog.ts
10828
- var import_fs11, import_path12, SkillCatalog;
10890
+ var import_fs12, import_path13, SkillCatalog;
10829
10891
  var init_skill_catalog = __esm({
10830
10892
  "packages/orchestrator/src/skill-catalog.ts"() {
10831
10893
  "use strict";
10832
- import_fs11 = require("fs");
10833
- import_path12 = require("path");
10894
+ import_fs12 = require("fs");
10895
+ import_path13 = require("path");
10834
10896
  init_skill_name();
10835
10897
  init_skill_parser();
10836
10898
  SkillCatalog = class {
@@ -10839,11 +10901,11 @@ var init_skill_catalog = __esm({
10839
10901
  projectSkillsDir;
10840
10902
  projectFileMtimes = /* @__PURE__ */ new Map();
10841
10903
  constructor(projectRoot, catalogPath) {
10842
- const defaultPath = catalogPath || (0, import_path12.resolve)(__dirname, "default-skills", "catalog.json");
10843
- this.defaultSkillsDir = (0, import_path12.resolve)(__dirname, "default-skills");
10844
- this.projectSkillsDir = projectRoot ? (0, import_path12.join)(projectRoot, ".gossip", "skills") : null;
10904
+ const defaultPath = catalogPath || (0, import_path13.resolve)(__dirname, "default-skills", "catalog.json");
10905
+ this.defaultSkillsDir = (0, import_path13.resolve)(__dirname, "default-skills");
10906
+ this.projectSkillsDir = projectRoot ? (0, import_path13.join)(projectRoot, ".gossip", "skills") : null;
10845
10907
  try {
10846
- const raw = (0, import_fs11.readFileSync)(defaultPath, "utf-8");
10908
+ const raw = (0, import_fs12.readFileSync)(defaultPath, "utf-8");
10847
10909
  const data = JSON.parse(raw);
10848
10910
  this.entries = data.skills.map((s) => ({
10849
10911
  ...s,
@@ -10884,7 +10946,7 @@ var init_skill_catalog = __esm({
10884
10946
  }
10885
10947
  validate() {
10886
10948
  const issues = [];
10887
- const mdFiles = (0, import_fs11.readdirSync)(this.defaultSkillsDir).filter((f) => f.endsWith(".md")).map((f) => normalizeSkillName(f.replace(".md", "")));
10949
+ const mdFiles = (0, import_fs12.readdirSync)(this.defaultSkillsDir).filter((f) => f.endsWith(".md")).map((f) => normalizeSkillName(f.replace(".md", "")));
10888
10950
  for (const file2 of mdFiles) {
10889
10951
  if (!this.entries.find((e) => e.name === file2)) {
10890
10952
  issues.push(`Skill file '${file2}' has no catalog entry`);
@@ -10893,15 +10955,15 @@ var init_skill_catalog = __esm({
10893
10955
  return issues;
10894
10956
  }
10895
10957
  loadProjectSkills() {
10896
- if (!this.projectSkillsDir || !(0, import_fs11.existsSync)(this.projectSkillsDir)) return;
10897
- const files = (0, import_fs11.readdirSync)(this.projectSkillsDir).filter((f) => f.endsWith(".md"));
10958
+ if (!this.projectSkillsDir || !(0, import_fs12.existsSync)(this.projectSkillsDir)) return;
10959
+ const files = (0, import_fs12.readdirSync)(this.projectSkillsDir).filter((f) => f.endsWith(".md"));
10898
10960
  const newMtimes = /* @__PURE__ */ new Map();
10899
10961
  for (const file2 of files) {
10900
- const filePath = (0, import_path12.join)(this.projectSkillsDir, file2);
10962
+ const filePath = (0, import_path13.join)(this.projectSkillsDir, file2);
10901
10963
  try {
10902
- const mtime = (0, import_fs11.statSync)(filePath).mtimeMs;
10964
+ const mtime = (0, import_fs12.statSync)(filePath).mtimeMs;
10903
10965
  newMtimes.set(file2, mtime);
10904
- const content = (0, import_fs11.readFileSync)(filePath, "utf-8");
10966
+ const content = (0, import_fs12.readFileSync)(filePath, "utf-8");
10905
10967
  const fm = parseSkillFrontmatter(content);
10906
10968
  if (!fm) continue;
10907
10969
  const entry = {
@@ -10923,14 +10985,14 @@ var init_skill_catalog = __esm({
10923
10985
  this.projectFileMtimes = newMtimes;
10924
10986
  }
10925
10987
  reloadIfChanged() {
10926
- if (!this.projectSkillsDir || !(0, import_fs11.existsSync)(this.projectSkillsDir)) return;
10927
- const files = (0, import_fs11.readdirSync)(this.projectSkillsDir).filter((f) => f.endsWith(".md"));
10988
+ if (!this.projectSkillsDir || !(0, import_fs12.existsSync)(this.projectSkillsDir)) return;
10989
+ const files = (0, import_fs12.readdirSync)(this.projectSkillsDir).filter((f) => f.endsWith(".md"));
10928
10990
  let changed = files.length !== this.projectFileMtimes.size;
10929
10991
  if (!changed) {
10930
10992
  for (const file2 of files) {
10931
- const filePath = (0, import_path12.join)(this.projectSkillsDir, file2);
10993
+ const filePath = (0, import_path13.join)(this.projectSkillsDir, file2);
10932
10994
  try {
10933
- const mtime = (0, import_fs11.statSync)(filePath).mtimeMs;
10995
+ const mtime = (0, import_fs12.statSync)(filePath).mtimeMs;
10934
10996
  if (mtime !== this.projectFileMtimes.get(file2)) {
10935
10997
  changed = true;
10936
10998
  break;
@@ -10951,12 +11013,12 @@ var init_skill_catalog = __esm({
10951
11013
  });
10952
11014
 
10953
11015
  // packages/orchestrator/src/skill-gap-tracker.ts
10954
- var import_fs12, import_path13, MAX_LOG_LINES, TRUNCATE_TO, SkillGapTracker;
11016
+ var import_fs13, import_path14, MAX_LOG_LINES, TRUNCATE_TO, SkillGapTracker;
10955
11017
  var init_skill_gap_tracker = __esm({
10956
11018
  "packages/orchestrator/src/skill-gap-tracker.ts"() {
10957
11019
  "use strict";
10958
- import_fs12 = require("fs");
10959
- import_path13 = require("path");
11020
+ import_fs13 = require("fs");
11021
+ import_path14 = require("path");
10960
11022
  init_skill_name();
10961
11023
  MAX_LOG_LINES = 5e3;
10962
11024
  TRUNCATE_TO = 1e3;
@@ -10965,8 +11027,8 @@ var init_skill_gap_tracker = __esm({
10965
11027
  resolutionsPath;
10966
11028
  resolutionsCache = null;
10967
11029
  constructor(projectRoot) {
10968
- this.gapLogPath = (0, import_path13.join)(projectRoot, ".gossip", "skill-gaps.jsonl");
10969
- this.resolutionsPath = (0, import_path13.join)(projectRoot, ".gossip", "skill-resolutions.json");
11030
+ this.gapLogPath = (0, import_path14.join)(projectRoot, ".gossip", "skill-gaps.jsonl");
11031
+ this.resolutionsPath = (0, import_path14.join)(projectRoot, ".gossip", "skill-resolutions.json");
10970
11032
  this.migrateResolutions();
10971
11033
  }
10972
11034
  checkThresholds() {
@@ -10994,9 +11056,9 @@ var init_skill_gap_tracker = __esm({
10994
11056
  const normalized = normalizeSkillName(skillName);
10995
11057
  const resolutions = this.loadResolutions();
10996
11058
  resolutions[normalized] = (/* @__PURE__ */ new Date()).toISOString();
10997
- const dir = (0, import_path13.join)(this.resolutionsPath, "..");
10998
- if (!(0, import_fs12.existsSync)(dir)) (0, import_fs12.mkdirSync)(dir, { recursive: true });
10999
- (0, import_fs12.writeFileSync)(this.resolutionsPath, JSON.stringify(resolutions, null, 2));
11059
+ const dir = (0, import_path14.join)(this.resolutionsPath, "..");
11060
+ if (!(0, import_fs13.existsSync)(dir)) (0, import_fs13.mkdirSync)(dir, { recursive: true });
11061
+ (0, import_fs13.writeFileSync)(this.resolutionsPath, JSON.stringify(resolutions, null, 2));
11000
11062
  this.resolutionsCache = resolutions;
11001
11063
  }
11002
11064
  getSuggestionsSince(agentId, sinceMs) {
@@ -11005,9 +11067,9 @@ var init_skill_gap_tracker = __esm({
11005
11067
  );
11006
11068
  }
11007
11069
  appendSuggestion(suggestion) {
11008
- const dir = (0, import_path13.join)(this.gapLogPath, "..");
11009
- if (!(0, import_fs12.existsSync)(dir)) (0, import_fs12.mkdirSync)(dir, { recursive: true });
11010
- (0, import_fs12.appendFileSync)(this.gapLogPath, JSON.stringify(suggestion) + "\n");
11070
+ const dir = (0, import_path14.join)(this.gapLogPath, "..");
11071
+ if (!(0, import_fs13.existsSync)(dir)) (0, import_fs13.mkdirSync)(dir, { recursive: true });
11072
+ (0, import_fs13.appendFileSync)(this.gapLogPath, JSON.stringify(suggestion) + "\n");
11011
11073
  this.truncateIfNeeded();
11012
11074
  }
11013
11075
  getPendingSkills() {
@@ -11035,9 +11097,9 @@ var init_skill_gap_tracker = __esm({
11035
11097
  );
11036
11098
  }
11037
11099
  readSuggestions() {
11038
- if (!(0, import_fs12.existsSync)(this.gapLogPath)) return [];
11100
+ if (!(0, import_fs13.existsSync)(this.gapLogPath)) return [];
11039
11101
  try {
11040
- const lines = (0, import_fs12.readFileSync)(this.gapLogPath, "utf-8").trim().split("\n").filter(Boolean);
11102
+ const lines = (0, import_fs13.readFileSync)(this.gapLogPath, "utf-8").trim().split("\n").filter(Boolean);
11041
11103
  return lines.map((line) => {
11042
11104
  try {
11043
11105
  return JSON.parse(line);
@@ -11051,12 +11113,12 @@ var init_skill_gap_tracker = __esm({
11051
11113
  }
11052
11114
  loadResolutions() {
11053
11115
  if (this.resolutionsCache) return this.resolutionsCache;
11054
- if (!(0, import_fs12.existsSync)(this.resolutionsPath)) {
11116
+ if (!(0, import_fs13.existsSync)(this.resolutionsPath)) {
11055
11117
  this.resolutionsCache = {};
11056
11118
  return this.resolutionsCache;
11057
11119
  }
11058
11120
  try {
11059
- this.resolutionsCache = JSON.parse((0, import_fs12.readFileSync)(this.resolutionsPath, "utf-8"));
11121
+ this.resolutionsCache = JSON.parse((0, import_fs13.readFileSync)(this.resolutionsPath, "utf-8"));
11060
11122
  return this.resolutionsCache;
11061
11123
  } catch {
11062
11124
  this.resolutionsCache = {};
@@ -11064,10 +11126,10 @@ var init_skill_gap_tracker = __esm({
11064
11126
  }
11065
11127
  }
11066
11128
  migrateResolutions() {
11067
- if ((0, import_fs12.existsSync)(this.resolutionsPath)) return;
11068
- if (!(0, import_fs12.existsSync)(this.gapLogPath)) return;
11129
+ if ((0, import_fs13.existsSync)(this.resolutionsPath)) return;
11130
+ if (!(0, import_fs13.existsSync)(this.gapLogPath)) return;
11069
11131
  try {
11070
- const lines = (0, import_fs12.readFileSync)(this.gapLogPath, "utf-8").trim().split("\n").filter(Boolean);
11132
+ const lines = (0, import_fs13.readFileSync)(this.gapLogPath, "utf-8").trim().split("\n").filter(Boolean);
11071
11133
  const resolutions = {};
11072
11134
  for (const line of lines) {
11073
11135
  try {
@@ -11079,21 +11141,21 @@ var init_skill_gap_tracker = __esm({
11079
11141
  }
11080
11142
  }
11081
11143
  if (Object.keys(resolutions).length > 0) {
11082
- const dir = (0, import_path13.join)(this.resolutionsPath, "..");
11083
- if (!(0, import_fs12.existsSync)(dir)) (0, import_fs12.mkdirSync)(dir, { recursive: true });
11084
- (0, import_fs12.writeFileSync)(this.resolutionsPath, JSON.stringify(resolutions, null, 2));
11144
+ const dir = (0, import_path14.join)(this.resolutionsPath, "..");
11145
+ if (!(0, import_fs13.existsSync)(dir)) (0, import_fs13.mkdirSync)(dir, { recursive: true });
11146
+ (0, import_fs13.writeFileSync)(this.resolutionsPath, JSON.stringify(resolutions, null, 2));
11085
11147
  this.resolutionsCache = resolutions;
11086
11148
  }
11087
11149
  } catch {
11088
11150
  }
11089
11151
  }
11090
11152
  truncateIfNeeded() {
11091
- if (!(0, import_fs12.existsSync)(this.gapLogPath)) return;
11153
+ if (!(0, import_fs13.existsSync)(this.gapLogPath)) return;
11092
11154
  try {
11093
- const content = (0, import_fs12.readFileSync)(this.gapLogPath, "utf-8");
11155
+ const content = (0, import_fs13.readFileSync)(this.gapLogPath, "utf-8");
11094
11156
  const lines = content.trim().split("\n").filter(Boolean);
11095
11157
  if (lines.length > MAX_LOG_LINES) {
11096
- (0, import_fs12.writeFileSync)(this.gapLogPath, lines.slice(-TRUNCATE_TO).join("\n") + "\n");
11158
+ (0, import_fs13.writeFileSync)(this.gapLogPath, lines.slice(-TRUNCATE_TO).join("\n") + "\n");
11097
11159
  }
11098
11160
  } catch {
11099
11161
  }
@@ -11103,12 +11165,12 @@ var init_skill_gap_tracker = __esm({
11103
11165
  });
11104
11166
 
11105
11167
  // packages/orchestrator/src/scope-tracker.ts
11106
- var import_path14, import_fs13, ScopeTracker;
11168
+ var import_path15, import_fs14, ScopeTracker;
11107
11169
  var init_scope_tracker = __esm({
11108
11170
  "packages/orchestrator/src/scope-tracker.ts"() {
11109
11171
  "use strict";
11110
- import_path14 = require("path");
11111
- import_fs13 = require("fs");
11172
+ import_path15 = require("path");
11173
+ import_fs14 = require("fs");
11112
11174
  ScopeTracker = class {
11113
11175
  // taskId → scope (for release)
11114
11176
  constructor(projectRoot) {
@@ -11119,15 +11181,15 @@ var init_scope_tracker = __esm({
11119
11181
  taskToScope = /* @__PURE__ */ new Map();
11120
11182
  normalize(scope) {
11121
11183
  if (!scope || !scope.trim()) throw new Error("Scope must not be empty");
11122
- const realRoot = (0, import_fs13.realpathSync)(this.projectRoot);
11123
- const abs = (0, import_path14.resolve)(realRoot, scope);
11184
+ const realRoot = (0, import_fs14.realpathSync)(this.projectRoot);
11185
+ const abs = (0, import_path15.resolve)(realRoot, scope);
11124
11186
  let real;
11125
11187
  try {
11126
- real = (0, import_fs13.realpathSync)(abs);
11188
+ real = (0, import_fs14.realpathSync)(abs);
11127
11189
  } catch {
11128
11190
  real = abs;
11129
11191
  }
11130
- const rel = (0, import_path14.relative)(realRoot, real);
11192
+ const rel = (0, import_path15.relative)(realRoot, real);
11131
11193
  if (rel.startsWith("..")) throw new Error(`Scope "${scope}" resolves outside project root`);
11132
11194
  if (rel === "") throw new Error(`Scope "${scope}" resolves to project root \u2014 too broad`);
11133
11195
  return rel.endsWith("/") ? rel : rel + "/";
@@ -11165,14 +11227,14 @@ var init_scope_tracker = __esm({
11165
11227
  });
11166
11228
 
11167
11229
  // packages/orchestrator/src/worktree-manager.ts
11168
- var import_child_process3, import_util8, import_promises2, import_path15, import_os, execFileAsync3, WorktreeManager;
11230
+ var import_child_process3, import_util8, import_promises2, import_path16, import_os, execFileAsync3, WorktreeManager;
11169
11231
  var init_worktree_manager = __esm({
11170
11232
  "packages/orchestrator/src/worktree-manager.ts"() {
11171
11233
  "use strict";
11172
11234
  import_child_process3 = require("child_process");
11173
11235
  import_util8 = require("util");
11174
11236
  import_promises2 = require("fs/promises");
11175
- import_path15 = require("path");
11237
+ import_path16 = require("path");
11176
11238
  import_os = require("os");
11177
11239
  execFileAsync3 = (0, import_util8.promisify)(import_child_process3.execFile);
11178
11240
  WorktreeManager = class {
@@ -11181,7 +11243,7 @@ var init_worktree_manager = __esm({
11181
11243
  }
11182
11244
  async create(taskId) {
11183
11245
  const branch = `gossip-${taskId}`;
11184
- const wtPath = await (0, import_promises2.mkdtemp)((0, import_path15.join)((0, import_os.tmpdir)(), "gossip-wt-"));
11246
+ const wtPath = await (0, import_promises2.mkdtemp)((0, import_path16.join)((0, import_os.tmpdir)(), "gossip-wt-"));
11185
11247
  await execFileAsync3("git", ["branch", branch, "HEAD"], { cwd: this.projectRoot });
11186
11248
  try {
11187
11249
  await execFileAsync3("git", ["worktree", "add", wtPath, branch], { cwd: this.projectRoot });
@@ -11256,12 +11318,13 @@ function getSeverityMultiplier(severity) {
11256
11318
  function clamp(v, min, max) {
11257
11319
  return Math.max(min, Math.min(max, v));
11258
11320
  }
11259
- var import_fs14, import_path16, CIRCUIT_BREAKER_THRESHOLD, NEGATIVE_SIGNALS, SIGNAL_EXPIRY_DAYS, KNOWN_SIGNALS, SEVERITY_MULTIPLIER, PerformanceReader;
11321
+ var import_fs15, import_path17, CIRCUIT_BREAKER_THRESHOLD, NEGATIVE_SIGNALS, SIGNAL_EXPIRY_DAYS, KNOWN_SIGNALS, SEVERITY_MULTIPLIER, PerformanceReader;
11260
11322
  var init_performance_reader = __esm({
11261
11323
  "packages/orchestrator/src/performance-reader.ts"() {
11262
11324
  "use strict";
11263
- import_fs14 = require("fs");
11264
- import_path16 = require("path");
11325
+ import_fs15 = require("fs");
11326
+ import_path17 = require("path");
11327
+ init_skill_name();
11265
11328
  CIRCUIT_BREAKER_THRESHOLD = 3;
11266
11329
  NEGATIVE_SIGNALS = /* @__PURE__ */ new Set(["hallucination_caught", "disagreement", "unique_unconfirmed"]);
11267
11330
  SIGNAL_EXPIRY_DAYS = 30;
@@ -11290,13 +11353,13 @@ var init_performance_reader = __esm({
11290
11353
  cachedScores = null;
11291
11354
  cachedMtimeMs = 0;
11292
11355
  constructor(projectRoot) {
11293
- this.filePath = (0, import_path16.join)(projectRoot, ".gossip", "agent-performance.jsonl");
11356
+ this.filePath = (0, import_path17.join)(projectRoot, ".gossip", "agent-performance.jsonl");
11294
11357
  }
11295
11358
  /** Read all signals and compute per-agent scores (cached by file mtime) */
11296
11359
  getScores() {
11297
11360
  let mtimeMs = 0;
11298
11361
  try {
11299
- mtimeMs = (0, import_fs14.statSync)(this.filePath).mtimeMs;
11362
+ mtimeMs = (0, import_fs15.statSync)(this.filePath).mtimeMs;
11300
11363
  } catch {
11301
11364
  }
11302
11365
  if (this.cachedScores && mtimeMs === this.cachedMtimeMs) {
@@ -11345,9 +11408,10 @@ var init_performance_reader = __esm({
11345
11408
  getCountersSince(agentId, category, sinceMs) {
11346
11409
  const allSignals = this.readSignalsRaw();
11347
11410
  const counters = { correct: 0, hallucinated: 0 };
11411
+ const normalizedTarget = normalizeSkillName(category);
11348
11412
  for (const s of allSignals) {
11349
11413
  if (s.agentId !== agentId) continue;
11350
- if (s.category !== category) continue;
11414
+ if (normalizeSkillName(s.category ?? "") !== normalizedTarget) continue;
11351
11415
  const ts = s.timestamp ? new Date(s.timestamp).getTime() : 0;
11352
11416
  if (!isFinite(ts) || ts === 0 || ts < sinceMs) continue;
11353
11417
  switch (s.signal) {
@@ -11372,9 +11436,9 @@ var init_performance_reader = __esm({
11372
11436
  * Don't unify these — see getCountersSince doc and consensus 9369ebfc-a3654b51 f1.
11373
11437
  */
11374
11438
  readSignalsRaw() {
11375
- if (!(0, import_fs14.existsSync)(this.filePath)) return [];
11439
+ if (!(0, import_fs15.existsSync)(this.filePath)) return [];
11376
11440
  try {
11377
- const lines = (0, import_fs14.readFileSync)(this.filePath, "utf-8").trim().split("\n").filter(Boolean);
11441
+ const lines = (0, import_fs15.readFileSync)(this.filePath, "utf-8").trim().split("\n").filter(Boolean);
11378
11442
  const all = lines.map((line) => {
11379
11443
  try {
11380
11444
  return JSON.parse(line);
@@ -11407,10 +11471,10 @@ var init_performance_reader = __esm({
11407
11471
  }
11408
11472
  }
11409
11473
  readSignals() {
11410
- if (!(0, import_fs14.existsSync)(this.filePath)) return [];
11474
+ if (!(0, import_fs15.existsSync)(this.filePath)) return [];
11411
11475
  try {
11412
11476
  const expiryMs = Date.now() - SIGNAL_EXPIRY_DAYS * 864e5;
11413
- const lines = (0, import_fs14.readFileSync)(this.filePath, "utf-8").trim().split("\n").filter(Boolean);
11477
+ const lines = (0, import_fs15.readFileSync)(this.filePath, "utf-8").trim().split("\n").filter(Boolean);
11414
11478
  const all = lines.map((line) => {
11415
11479
  try {
11416
11480
  return JSON.parse(line);
@@ -11622,6 +11686,7 @@ var init_performance_reader = __esm({
11622
11686
  const timeFreshness = Math.pow(0.5, daysSinceLastSignal / halfLife);
11623
11687
  reliability = 0.5 + (reliability - 0.5) * timeFreshness;
11624
11688
  }
11689
+ const MIN_CATEGORY_N = 5;
11625
11690
  const categoryAccuracy = {};
11626
11691
  const allCategories = /* @__PURE__ */ new Set([
11627
11692
  ...Object.keys(a.categoryCorrect),
@@ -11630,7 +11695,7 @@ var init_performance_reader = __esm({
11630
11695
  for (const cat of allCategories) {
11631
11696
  const c = a.categoryCorrect[cat] ?? 0;
11632
11697
  const h = a.categoryHallucinated[cat] ?? 0;
11633
- if (c + h > 0) categoryAccuracy[cat] = c / (c + h);
11698
+ if (c + h >= MIN_CATEGORY_N) categoryAccuracy[cat] = c / (c + h);
11634
11699
  }
11635
11700
  const consec = consecutiveFailures.get(id) || 0;
11636
11701
  scores.set(id, {
@@ -11675,11 +11740,11 @@ var init_performance_reader = __esm({
11675
11740
  return result;
11676
11741
  }
11677
11742
  getImplScore(agentId) {
11678
- if (!(0, import_fs14.existsSync)(this.filePath)) return null;
11743
+ if (!(0, import_fs15.existsSync)(this.filePath)) return null;
11679
11744
  try {
11680
11745
  const now = Date.now();
11681
11746
  const expiryMs = now - SIGNAL_EXPIRY_DAYS * 864e5;
11682
- const lines = (0, import_fs14.readFileSync)(this.filePath, "utf-8").trim().split("\n").filter(Boolean);
11747
+ const lines = (0, import_fs15.readFileSync)(this.filePath, "utf-8").trim().split("\n").filter(Boolean);
11683
11748
  let pass = 0, fail = 0, approved = 0, rejected = 0, lastImplSignalMs = 0;
11684
11749
  for (const line of lines) {
11685
11750
  try {
@@ -11718,12 +11783,12 @@ var init_performance_reader = __esm({
11718
11783
  });
11719
11784
 
11720
11785
  // packages/orchestrator/src/skill-counters.ts
11721
- var import_fs15, import_path17, STALE_THRESHOLD, PROMOTION_RATE, PROMOTION_MIN_WINDOW, SkillCounterTracker;
11786
+ var import_fs16, import_path18, STALE_THRESHOLD, PROMOTION_RATE, PROMOTION_MIN_WINDOW, SkillCounterTracker;
11722
11787
  var init_skill_counters = __esm({
11723
11788
  "packages/orchestrator/src/skill-counters.ts"() {
11724
11789
  "use strict";
11725
- import_fs15 = require("fs");
11726
- import_path17 = require("path");
11790
+ import_fs16 = require("fs");
11791
+ import_path18 = require("path");
11727
11792
  STALE_THRESHOLD = 30;
11728
11793
  PROMOTION_RATE = 0.8;
11729
11794
  PROMOTION_MIN_WINDOW = 20;
@@ -11732,7 +11797,7 @@ var init_skill_counters = __esm({
11732
11797
  filePath;
11733
11798
  dirty = false;
11734
11799
  constructor(projectRoot) {
11735
- this.filePath = (0, import_path17.join)(projectRoot, ".gossip", "skill-counters.json");
11800
+ this.filePath = (0, import_path18.join)(projectRoot, ".gossip", "skill-counters.json");
11736
11801
  this.load();
11737
11802
  }
11738
11803
  /**
@@ -11807,15 +11872,15 @@ var init_skill_counters = __esm({
11807
11872
  /** Flush counters to disk. Call during gossip_collect. */
11808
11873
  flush() {
11809
11874
  if (!this.dirty) return;
11810
- const dir = (0, import_path17.dirname)(this.filePath);
11811
- if (!(0, import_fs15.existsSync)(dir)) (0, import_fs15.mkdirSync)(dir, { recursive: true });
11812
- (0, import_fs15.writeFileSync)(this.filePath, JSON.stringify(this.data, null, 2) + "\n");
11875
+ const dir = (0, import_path18.dirname)(this.filePath);
11876
+ if (!(0, import_fs16.existsSync)(dir)) (0, import_fs16.mkdirSync)(dir, { recursive: true });
11877
+ (0, import_fs16.writeFileSync)(this.filePath, JSON.stringify(this.data, null, 2) + "\n");
11813
11878
  this.dirty = false;
11814
11879
  }
11815
11880
  load() {
11816
11881
  try {
11817
- if ((0, import_fs15.existsSync)(this.filePath)) {
11818
- const raw = JSON.parse((0, import_fs15.readFileSync)(this.filePath, "utf-8"));
11882
+ if ((0, import_fs16.existsSync)(this.filePath)) {
11883
+ const raw = JSON.parse((0, import_fs16.readFileSync)(this.filePath, "utf-8"));
11819
11884
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
11820
11885
  for (const [agentId, skills] of Object.entries(raw)) {
11821
11886
  if (!skills || typeof skills !== "object") {
@@ -11842,14 +11907,14 @@ function shortConsensusId() {
11842
11907
  const hex3 = (0, import_crypto6.randomUUID)().replace(/-/g, "");
11843
11908
  return hex3.slice(0, 8) + "-" + hex3.slice(8, 16);
11844
11909
  }
11845
- var import_promises3, import_fs16, import_crypto6, import_path18, SUMMARY_HEADER, FALLBACK_MAX_LENGTH, MAX_SUMMARY_LENGTH, MAX_CROSS_REVIEW_ENTRIES, VALID_ACTIONS, ANCHOR_PATTERN, ConsensusEngine;
11910
+ var import_promises3, import_fs17, import_crypto6, import_path19, SUMMARY_HEADER, FALLBACK_MAX_LENGTH, MAX_SUMMARY_LENGTH, MAX_CROSS_REVIEW_ENTRIES, VALID_ACTIONS, ANCHOR_PATTERN, ConsensusEngine;
11846
11911
  var init_consensus_engine = __esm({
11847
11912
  "packages/orchestrator/src/consensus-engine.ts"() {
11848
11913
  "use strict";
11849
11914
  import_promises3 = require("fs/promises");
11850
- import_fs16 = require("fs");
11915
+ import_fs17 = require("fs");
11851
11916
  import_crypto6 = require("crypto");
11852
- import_path18 = require("path");
11917
+ import_path19 = require("path");
11853
11918
  SUMMARY_HEADER = "## Consensus Summary";
11854
11919
  FALLBACK_MAX_LENGTH = 2e3;
11855
11920
  MAX_SUMMARY_LENGTH = 5e3;
@@ -11885,7 +11950,7 @@ var init_consensus_engine = __esm({
11885
11950
  for (const r of results) {
11886
11951
  const wt = r.worktreeInfo?.path;
11887
11952
  if (wt && typeof wt === "string") {
11888
- next.add((0, import_path18.resolve)(wt));
11953
+ next.add((0, import_path19.resolve)(wt));
11889
11954
  }
11890
11955
  }
11891
11956
  let changed = next.size !== this.currentWorktreeRoots.size;
@@ -12713,9 +12778,9 @@ ${safeSnippet}
12713
12778
  */
12714
12779
  /** Guard: resolved path must stay inside one of the valid roots */
12715
12780
  isInsideAnyRoot(candidate, roots) {
12716
- const normalized = (0, import_path18.resolve)(candidate);
12781
+ const normalized = (0, import_path19.resolve)(candidate);
12717
12782
  return roots.some((root) => {
12718
- const normalizedRoot = (0, import_path18.resolve)(root);
12783
+ const normalizedRoot = (0, import_path19.resolve)(root);
12719
12784
  return normalized === normalizedRoot || normalized.startsWith(normalizedRoot + "/");
12720
12785
  });
12721
12786
  }
@@ -12725,7 +12790,7 @@ ${safeSnippet}
12725
12790
  const fileName = fileRef.split("/").pop();
12726
12791
  for (const root of roots) {
12727
12792
  try {
12728
- const candidate = (0, import_path18.join)(root, fileRef);
12793
+ const candidate = (0, import_path19.join)(root, fileRef);
12729
12794
  if (this.isInsideAnyRoot(candidate, roots)) {
12730
12795
  await (0, import_promises3.stat)(candidate);
12731
12796
  return candidate;
@@ -12734,7 +12799,7 @@ ${safeSnippet}
12734
12799
  }
12735
12800
  if (fileName !== fileRef) {
12736
12801
  try {
12737
- const candidate = (0, import_path18.join)(root, fileName);
12802
+ const candidate = (0, import_path19.join)(root, fileName);
12738
12803
  if (this.isInsideAnyRoot(candidate, roots)) {
12739
12804
  await (0, import_promises3.stat)(candidate);
12740
12805
  return candidate;
@@ -12744,7 +12809,7 @@ ${safeSnippet}
12744
12809
  }
12745
12810
  const searchDirs = ["packages", "src", "apps", "tests", "test", "tools", "scripts", "lib"];
12746
12811
  for (const dir of searchDirs) {
12747
- const found = await this.findFile((0, import_path18.join)(root, dir), fileName, roots);
12812
+ const found = await this.findFile((0, import_path19.join)(root, dir), fileName, roots);
12748
12813
  if (found) return found;
12749
12814
  }
12750
12815
  }
@@ -12754,7 +12819,7 @@ ${safeSnippet}
12754
12819
  try {
12755
12820
  const entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
12756
12821
  for (const entry of entries) {
12757
- const fullPath = (0, import_path18.join)(dir, entry.name);
12822
+ const fullPath = (0, import_path19.join)(dir, entry.name);
12758
12823
  if (entry.isFile() && entry.name === fileName) {
12759
12824
  if (!this.isInsideAnyRoot(fullPath, validRoots)) return null;
12760
12825
  return fullPath;
@@ -12780,7 +12845,7 @@ ${safeSnippet}
12780
12845
  const sourceExts = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
12781
12846
  for (const root of roots) {
12782
12847
  for (const dir of searchDirs) {
12783
- const result = await this.grepDir((0, import_path18.join)(root, dir), identifier, sourceExts, CONTEXT_LINES);
12848
+ const result = await this.grepDir((0, import_path19.join)(root, dir), identifier, sourceExts, CONTEXT_LINES);
12784
12849
  if (result) return result;
12785
12850
  }
12786
12851
  }
@@ -12791,7 +12856,7 @@ ${safeSnippet}
12791
12856
  try {
12792
12857
  const entries = await (0, import_promises3.readdir)(dir, { withFileTypes: true });
12793
12858
  for (const entry of entries) {
12794
- const fullPath = (0, import_path18.join)(dir, entry.name);
12859
+ const fullPath = (0, import_path19.join)(dir, entry.name);
12795
12860
  if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git" && entry.name !== "dist") {
12796
12861
  const found = await this.grepDir(fullPath, identifier, exts, contextLines);
12797
12862
  if (found) return found;
@@ -13285,11 +13350,11 @@ ${safeSnippet}
13285
13350
  dumpFailedCrossReview(reviewerAgentId, text) {
13286
13351
  if (!this.config.projectRoot) return;
13287
13352
  try {
13288
- const dir = (0, import_path18.join)(this.config.projectRoot, ".gossip", "cross-review-failures");
13289
- (0, import_fs16.mkdirSync)(dir, { recursive: true });
13353
+ const dir = (0, import_path19.join)(this.config.projectRoot, ".gossip", "cross-review-failures");
13354
+ (0, import_fs17.mkdirSync)(dir, { recursive: true });
13290
13355
  const safeId2 = reviewerAgentId.replace(/[^a-zA-Z0-9_-]/g, "_");
13291
13356
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
13292
- (0, import_fs16.writeFileSync)((0, import_path18.join)(dir, `${safeId2}-${ts}.txt`), text);
13357
+ (0, import_fs17.writeFileSync)((0, import_path19.join)(dir, `${safeId2}-${ts}.txt`), text);
13293
13358
  } catch {
13294
13359
  }
13295
13360
  }
@@ -13362,12 +13427,12 @@ function validateSignal(signal) {
13362
13427
  throw new Error(`Signal validation failed: unknown type "${signal.type}"`);
13363
13428
  }
13364
13429
  }
13365
- var import_fs17, import_path19, VALID_CONSENSUS_SIGNALS, VALID_IMPL_SIGNALS, VALID_META_SIGNALS, PerformanceWriter;
13430
+ var import_fs18, import_path20, VALID_CONSENSUS_SIGNALS, VALID_IMPL_SIGNALS, VALID_META_SIGNALS, PerformanceWriter;
13366
13431
  var init_performance_writer = __esm({
13367
13432
  "packages/orchestrator/src/performance-writer.ts"() {
13368
13433
  "use strict";
13369
- import_fs17 = require("fs");
13370
- import_path19 = require("path");
13434
+ import_fs18 = require("fs");
13435
+ import_path20 = require("path");
13371
13436
  VALID_CONSENSUS_SIGNALS = /* @__PURE__ */ new Set([
13372
13437
  "agreement",
13373
13438
  "disagreement",
@@ -13393,19 +13458,19 @@ var init_performance_writer = __esm({
13393
13458
  PerformanceWriter = class {
13394
13459
  filePath;
13395
13460
  constructor(projectRoot) {
13396
- const dir = (0, import_path19.join)(projectRoot, ".gossip");
13397
- if (!(0, import_fs17.existsSync)(dir)) (0, import_fs17.mkdirSync)(dir, { recursive: true });
13398
- this.filePath = (0, import_path19.join)(dir, "agent-performance.jsonl");
13461
+ const dir = (0, import_path20.join)(projectRoot, ".gossip");
13462
+ if (!(0, import_fs18.existsSync)(dir)) (0, import_fs18.mkdirSync)(dir, { recursive: true });
13463
+ this.filePath = (0, import_path20.join)(dir, "agent-performance.jsonl");
13399
13464
  }
13400
13465
  appendSignal(signal) {
13401
13466
  validateSignal(signal);
13402
- (0, import_fs17.appendFileSync)(this.filePath, JSON.stringify(signal) + "\n");
13467
+ (0, import_fs18.appendFileSync)(this.filePath, JSON.stringify(signal) + "\n");
13403
13468
  }
13404
13469
  appendSignals(signals) {
13405
13470
  if (signals.length === 0) return;
13406
13471
  for (const s of signals) validateSignal(s);
13407
13472
  const data = signals.map((s) => JSON.stringify(s)).join("\n") + "\n";
13408
- (0, import_fs17.appendFileSync)(this.filePath, data);
13473
+ (0, import_fs18.appendFileSync)(this.filePath, data);
13409
13474
  }
13410
13475
  };
13411
13476
  }
@@ -13604,12 +13669,12 @@ ${topFindings}`;
13604
13669
  });
13605
13670
 
13606
13671
  // packages/orchestrator/src/session-context.ts
13607
- var import_fs18, import_path20, log3, SessionContext;
13672
+ var import_fs19, import_path21, log3, SessionContext;
13608
13673
  var init_session_context = __esm({
13609
13674
  "packages/orchestrator/src/session-context.ts"() {
13610
13675
  "use strict";
13611
- import_fs18 = require("fs");
13612
- import_path20 = require("path");
13676
+ import_fs19 = require("fs");
13677
+ import_path21 = require("path");
13613
13678
  log3 = (msg) => process.stderr.write(`[gossipcat] ${msg}
13614
13679
  `);
13615
13680
  SessionContext = class _SessionContext {
@@ -13623,7 +13688,7 @@ var init_session_context = __esm({
13623
13688
  this.projectRoot = config2.projectRoot;
13624
13689
  this.llm = config2.llm;
13625
13690
  try {
13626
- const gossipPath = (0, import_path20.join)(config2.projectRoot, ".gossip", "agents", "_project", "memory", "session-gossip.jsonl");
13691
+ const gossipPath = (0, import_path21.join)(config2.projectRoot, ".gossip", "agents", "_project", "memory", "session-gossip.jsonl");
13627
13692
  const { existsSync: ex, readFileSync: rf } = require("fs");
13628
13693
  if (ex(gossipPath)) {
13629
13694
  const lines = rf(gossipPath, "utf-8").trim().split("\n").filter(Boolean);
@@ -13675,9 +13740,9 @@ var init_session_context = __esm({
13675
13740
  this.sessionGossip.shift();
13676
13741
  }
13677
13742
  try {
13678
- const gossipPath = (0, import_path20.join)(this.projectRoot, ".gossip", "agents", "_project", "memory", "session-gossip.jsonl");
13679
- (0, import_fs18.mkdirSync)((0, import_path20.dirname)(gossipPath), { recursive: true });
13680
- (0, import_fs18.appendFileSync)(gossipPath, JSON.stringify({ agentId, taskSummary: summary, timestamp: Date.now() }) + "\n");
13743
+ const gossipPath = (0, import_path21.join)(this.projectRoot, ".gossip", "agents", "_project", "memory", "session-gossip.jsonl");
13744
+ (0, import_fs19.mkdirSync)((0, import_path21.dirname)(gossipPath), { recursive: true });
13745
+ (0, import_fs19.appendFileSync)(gossipPath, JSON.stringify({ agentId, taskSummary: summary, timestamp: Date.now() }) + "\n");
13681
13746
  this.rotateJsonlFile(gossipPath, 100, 50);
13682
13747
  } catch {
13683
13748
  }
@@ -13698,10 +13763,10 @@ ${result.slice(0, 2e3)}` }
13698
13763
  /** Rotate a JSONL file: if over maxEntries lines, keep only the last keepEntries. */
13699
13764
  rotateJsonlFile(filePath, maxEntries, keepEntries) {
13700
13765
  try {
13701
- const content = (0, import_fs18.readFileSync)(filePath, "utf-8");
13766
+ const content = (0, import_fs19.readFileSync)(filePath, "utf-8");
13702
13767
  const lines = content.trim().split("\n").filter((l) => l.length > 0);
13703
13768
  if (lines.length > maxEntries) {
13704
- (0, import_fs18.writeFileSync)(filePath, lines.slice(-keepEntries).join("\n") + "\n");
13769
+ (0, import_fs19.writeFileSync)(filePath, lines.slice(-keepEntries).join("\n") + "\n");
13705
13770
  }
13706
13771
  } catch {
13707
13772
  }
@@ -13720,13 +13785,13 @@ function shouldSkipConsensus(task, agents, costMode, agreementHistory) {
13720
13785
  const firstWord = task.trim().split(/\s+/)[0] || "";
13721
13786
  return OBSERVATION_VERBS.test(firstWord);
13722
13787
  }
13723
- var import_crypto8, import_fs19, import_path21, log4, DispatchPipeline, SECURITY_KEYWORDS, OBSERVATION_VERBS;
13788
+ var import_crypto8, import_fs20, import_path22, log4, DispatchPipeline, SECURITY_KEYWORDS, OBSERVATION_VERBS;
13724
13789
  var init_dispatch_pipeline = __esm({
13725
13790
  "packages/orchestrator/src/dispatch-pipeline.ts"() {
13726
13791
  "use strict";
13727
13792
  import_crypto8 = require("crypto");
13728
- import_fs19 = require("fs");
13729
- import_path21 = require("path");
13793
+ import_fs20 = require("fs");
13794
+ import_path22 = require("path");
13730
13795
  init_types2();
13731
13796
  init_skill_loader();
13732
13797
  init_prompt_assembler();
@@ -13815,8 +13880,8 @@ var init_dispatch_pipeline = __esm({
13815
13880
  this.sessionContext = new SessionContext({ llm: config2.llm ?? null, projectRoot: config2.projectRoot });
13816
13881
  this.worktreeManager.pruneOrphans().catch((err) => log4(`Orphan cleanup failed: ${err.message}`));
13817
13882
  try {
13818
- const projectMemDir = (0, import_path21.join)(config2.projectRoot, ".gossip", "agents", "_project", "memory");
13819
- (0, import_fs19.mkdirSync)(projectMemDir, { recursive: true });
13883
+ const projectMemDir = (0, import_path22.join)(config2.projectRoot, ".gossip", "agents", "_project", "memory");
13884
+ (0, import_fs20.mkdirSync)(projectMemDir, { recursive: true });
13820
13885
  } catch {
13821
13886
  }
13822
13887
  }
@@ -13887,11 +13952,11 @@ var init_dispatch_pipeline = __esm({
13887
13952
  const specRefs = extractSpecReferences(task);
13888
13953
  if (specRefs.length > 0) {
13889
13954
  try {
13890
- const specPath = (0, import_path21.resolve)(this.projectRoot, specRefs[0]);
13891
- const realSpecPath = (0, import_fs19.realpathSync)(specPath);
13892
- const realRoot = (0, import_fs19.realpathSync)(this.projectRoot);
13955
+ const specPath = (0, import_path22.resolve)(this.projectRoot, specRefs[0]);
13956
+ const realSpecPath = (0, import_fs20.realpathSync)(specPath);
13957
+ const realRoot = (0, import_fs20.realpathSync)(this.projectRoot);
13893
13958
  if (realSpecPath.startsWith(realRoot + "/")) {
13894
- const specContent = (0, import_fs19.readFileSync)(realSpecPath, "utf-8");
13959
+ const specContent = (0, import_fs20.readFileSync)(realSpecPath, "utf-8");
13895
13960
  const implFiles = extractSpecReferences(task, specContent);
13896
13961
  const enrichment = buildSpecReviewEnrichment(implFiles);
13897
13962
  if (enrichment) specReviewContext = enrichment;
@@ -13899,7 +13964,7 @@ var init_dispatch_pipeline = __esm({
13899
13964
  } catch {
13900
13965
  }
13901
13966
  }
13902
- const memoryDir = (0, import_path21.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "knowledge");
13967
+ const memoryDir = (0, import_path22.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "knowledge");
13903
13968
  const promptContent = assemblePrompt({
13904
13969
  memory: memory || void 0,
13905
13970
  memoryDir,
@@ -13959,7 +14024,7 @@ var init_dispatch_pipeline = __esm({
13959
14024
  const elapsedMs = (entry.completedAt ?? Date.now()) - entry.startedAt;
13960
14025
  process.stderr.write(`[gossipcat] \u2705 relay \u2190 ${entry.agentId} [${entry.id}] OK (${(elapsedMs / 1e3).toFixed(1)}s, ${(event.payload.result || "").length} chars)
13961
14026
  `);
13962
- (0, import_fs19.appendFileSync)((0, import_path21.join)(this.projectRoot, ".gossip", "task-graph.jsonl"), JSON.stringify({
14027
+ (0, import_fs20.appendFileSync)((0, import_path22.join)(this.projectRoot, ".gossip", "task-graph.jsonl"), JSON.stringify({
13963
14028
  type: "task.completed",
13964
14029
  taskId: entry.id,
13965
14030
  agentId: entry.agentId,
@@ -13983,7 +14048,7 @@ var init_dispatch_pipeline = __esm({
13983
14048
  const elapsedMs = (entry.completedAt ?? Date.now()) - entry.startedAt;
13984
14049
  process.stderr.write(`[gossipcat] \u274C relay \u2190 ${entry.agentId} [${entry.id}] FAILED (${(elapsedMs / 1e3).toFixed(1)}s) \u2014 ${event.payload.error}
13985
14050
  `);
13986
- (0, import_fs19.appendFileSync)((0, import_path21.join)(this.projectRoot, ".gossip", "task-graph.jsonl"), JSON.stringify({
14051
+ (0, import_fs20.appendFileSync)((0, import_path22.join)(this.projectRoot, ".gossip", "task-graph.jsonl"), JSON.stringify({
13987
14052
  type: "task.failed",
13988
14053
  taskId: entry.id,
13989
14054
  agentId: entry.agentId,
@@ -14910,13 +14975,13 @@ function parseYamlLikeToolCall(content) {
14910
14975
  const args = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
14911
14976
  return { tool, args };
14912
14977
  }
14913
- var import_fs20, import_path22, log5, AGENT_ID_RE, TAG_PATTERN, BLOCK_RE, BLOCK_IN_FENCE_RE, ToolRouter, ToolExecutor;
14978
+ var import_fs21, import_path23, log5, AGENT_ID_RE, TAG_PATTERN, BLOCK_RE, BLOCK_IN_FENCE_RE, ToolRouter, ToolExecutor;
14914
14979
  var init_tool_router = __esm({
14915
14980
  "packages/orchestrator/src/tool-router.ts"() {
14916
14981
  "use strict";
14917
14982
  init_tool_definitions();
14918
- import_fs20 = require("fs");
14919
- import_path22 = require("path");
14983
+ import_fs21 = require("fs");
14984
+ import_path23 = require("path");
14920
14985
  log5 = (msg) => process.stderr.write(`[tool-router] ${msg}
14921
14986
  `);
14922
14987
  AGENT_ID_RE = /^[a-zA-Z0-9_-]+$/;
@@ -15322,12 +15387,12 @@ ${agentOutputs}` }
15322
15387
  const fsp = await import("fs/promises");
15323
15388
  const updatedIds = [];
15324
15389
  for (const id of pending.agentIds) {
15325
- const agentDir = (0, import_path22.join)(this.projectRoot, ".gossip", "agents", id);
15326
- const filePath = (0, import_path22.join)(agentDir, "instructions.md");
15327
- if (!(0, import_fs20.existsSync)(agentDir)) {
15390
+ const agentDir = (0, import_path23.join)(this.projectRoot, ".gossip", "agents", id);
15391
+ const filePath = (0, import_path23.join)(agentDir, "instructions.md");
15392
+ if (!(0, import_fs21.existsSync)(agentDir)) {
15328
15393
  await fsp.mkdir(agentDir, { recursive: true });
15329
15394
  }
15330
- if ((0, import_fs20.existsSync)(filePath)) {
15395
+ if ((0, import_fs21.existsSync)(filePath)) {
15331
15396
  await fsp.appendFile(filePath, `
15332
15397
 
15333
15398
  ${pending.instruction}`, "utf-8");
@@ -15441,9 +15506,9 @@ ${collectResult.consensus.summary}`;
15441
15506
  async handlePlan(args) {
15442
15507
  let task = String(args.task);
15443
15508
  try {
15444
- const specPath = (0, import_path22.join)(this.projectRoot, ".gossip", "spec.md");
15445
- if ((0, import_fs20.existsSync)(specPath)) {
15446
- const spec = (0, import_fs20.readFileSync)(specPath, "utf-8");
15509
+ const specPath = (0, import_path23.join)(this.projectRoot, ".gossip", "spec.md");
15510
+ if ((0, import_fs21.existsSync)(specPath)) {
15511
+ const spec = (0, import_fs21.readFileSync)(specPath, "utf-8");
15447
15512
  task = `${task}
15448
15513
 
15449
15514
  [Project Spec]
@@ -15501,11 +15566,11 @@ ${taskLines.join("\n")}`,
15501
15566
  if (!this.llm) {
15502
15567
  return { text: "Tool error: LLM not available for spec generation" };
15503
15568
  }
15504
- const specPath = (0, import_path22.join)(this.projectRoot, ".gossip", "spec.md");
15569
+ const specPath = (0, import_path23.join)(this.projectRoot, ".gossip", "spec.md");
15505
15570
  let existingSpec = "";
15506
15571
  try {
15507
- if ((0, import_fs20.existsSync)(specPath)) {
15508
- existingSpec = (0, import_fs20.readFileSync)(specPath, "utf-8");
15572
+ if ((0, import_fs21.existsSync)(specPath)) {
15573
+ existingSpec = (0, import_fs21.readFileSync)(specPath, "utf-8");
15509
15574
  }
15510
15575
  } catch {
15511
15576
  }
@@ -15540,8 +15605,8 @@ Keep it SHORT \u2014 under 30 lines. This is a working document, not a design do
15540
15605
  ]);
15541
15606
  const specContent = response.text || "";
15542
15607
  try {
15543
- const { mkdirSync: mkdirSync21, writeFileSync: writeFS } = require("fs");
15544
- mkdirSync21((0, import_path22.join)(this.projectRoot, ".gossip"), { recursive: true });
15608
+ const { mkdirSync: mkdirSync22, writeFileSync: writeFS } = require("fs");
15609
+ mkdirSync22((0, import_path23.join)(this.projectRoot, ".gossip"), { recursive: true });
15545
15610
  writeFS(specPath, specContent, "utf-8");
15546
15611
  } catch (err) {
15547
15612
  return { text: `Spec generated but failed to save: ${err.message}
@@ -15579,11 +15644,11 @@ ${lines.join("\n")}` };
15579
15644
  if (!this.registry.get(agentId)) {
15580
15645
  return { text: `Tool error: agent "${agentId}" not found in registry` };
15581
15646
  }
15582
- const tasksPath = (0, import_path22.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "tasks.jsonl");
15583
- if (!(0, import_fs20.existsSync)(tasksPath)) {
15647
+ const tasksPath = (0, import_path23.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "tasks.jsonl");
15648
+ if (!(0, import_fs21.existsSync)(tasksPath)) {
15584
15649
  return { text: `Agent "${agentId}" \u2014 no task history found.` };
15585
15650
  }
15586
- const rawLines = (0, import_fs20.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
15651
+ const rawLines = (0, import_fs21.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
15587
15652
  const last5 = rawLines.slice(-5).map((line) => {
15588
15653
  try {
15589
15654
  return JSON.parse(line);
@@ -15600,11 +15665,11 @@ Last ${last5.length} tasks:
15600
15665
  ${formatted.join("\n")}` };
15601
15666
  }
15602
15667
  handleAgentPerformance() {
15603
- const perfPath = (0, import_path22.join)(this.projectRoot, ".gossip", "agent-performance.jsonl");
15604
- if (!(0, import_fs20.existsSync)(perfPath)) {
15668
+ const perfPath = (0, import_path23.join)(this.projectRoot, ".gossip", "agent-performance.jsonl");
15669
+ if (!(0, import_fs21.existsSync)(perfPath)) {
15605
15670
  return { text: "No performance data found." };
15606
15671
  }
15607
- const rawLines = (0, import_fs20.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
15672
+ const rawLines = (0, import_fs21.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
15608
15673
  const last20 = rawLines.slice(-20).map((line) => {
15609
15674
  try {
15610
15675
  return JSON.parse(line);
@@ -15655,11 +15720,11 @@ Instruction: "${instruction}"`,
15655
15720
  if (!this.registry.get(agentId)) {
15656
15721
  return { text: `Tool error: agent "${agentId}" not found in registry` };
15657
15722
  }
15658
- const tasksPath = (0, import_path22.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "tasks.jsonl");
15659
- if (!(0, import_fs20.existsSync)(tasksPath)) {
15723
+ const tasksPath = (0, import_path23.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "tasks.jsonl");
15724
+ if (!(0, import_fs21.existsSync)(tasksPath)) {
15660
15725
  return { text: `No task history for agent "${agentId}".` };
15661
15726
  }
15662
- const rawLines = (0, import_fs20.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
15727
+ const rawLines = (0, import_fs21.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
15663
15728
  const entries = rawLines.slice(-limit).map((line) => {
15664
15729
  try {
15665
15730
  return JSON.parse(line);
@@ -15719,17 +15784,34 @@ ${formatted.join("\n")}` };
15719
15784
  });
15720
15785
 
15721
15786
  // packages/orchestrator/src/archetype-catalog.ts
15722
- var import_fs21, import_path23, DEFAULT_PATH, ArchetypeCatalog;
15787
+ function findArchetypeCatalog() {
15788
+ const candidates = [
15789
+ (0, import_path24.resolve)(__dirname, "data", "archetypes.json"),
15790
+ // bundled prod: dist-mcp/data/archetypes.json
15791
+ (0, import_path24.resolve)(__dirname, "..", "..", "..", "data", "archetypes.json"),
15792
+ // dev ts-node: packages/orchestrator/src → repo/data
15793
+ (0, import_path24.resolve)(process.cwd(), "data", "archetypes.json")
15794
+ // last-resort dev fallback
15795
+ ];
15796
+ for (const p of candidates) {
15797
+ if ((0, import_fs22.existsSync)(p)) return p;
15798
+ }
15799
+ return null;
15800
+ }
15801
+ var import_fs22, import_path24, ArchetypeCatalog;
15723
15802
  var init_archetype_catalog = __esm({
15724
15803
  "packages/orchestrator/src/archetype-catalog.ts"() {
15725
15804
  "use strict";
15726
- import_fs21 = require("fs");
15727
- import_path23 = require("path");
15728
- DEFAULT_PATH = (0, import_path23.resolve)(__dirname, "..", "..", "..", "data", "archetypes.json");
15805
+ import_fs22 = require("fs");
15806
+ import_path24 = require("path");
15729
15807
  ArchetypeCatalog = class {
15730
15808
  archetypes;
15731
15809
  constructor(catalogPath) {
15732
- const raw = (0, import_fs21.readFileSync)(catalogPath ?? DEFAULT_PATH, "utf-8");
15810
+ const path2 = catalogPath ?? findArchetypeCatalog();
15811
+ if (!path2) {
15812
+ throw new Error("archetypes.json not found \u2014 tried bundled dist-mcp/data/, dev ts-node path, and cwd/data/. Reinstall gossipcat.");
15813
+ }
15814
+ const raw = (0, import_fs22.readFileSync)(path2, "utf-8");
15733
15815
  this.archetypes = JSON.parse(raw);
15734
15816
  }
15735
15817
  /** Return all archetype IDs */
@@ -15779,13 +15861,13 @@ var init_archetype_catalog = __esm({
15779
15861
  });
15780
15862
 
15781
15863
  // packages/orchestrator/src/project-initializer.ts
15782
- var import_fs22, import_path24, SIGNAL_DIRS, SIGNAL_FILES, LANG_FILES, ProjectInitializer;
15864
+ var import_fs23, import_path25, SIGNAL_DIRS, SIGNAL_FILES, LANG_FILES, ProjectInitializer;
15783
15865
  var init_project_initializer = __esm({
15784
15866
  "packages/orchestrator/src/project-initializer.ts"() {
15785
15867
  "use strict";
15786
15868
  init_archetype_catalog();
15787
- import_fs22 = require("fs");
15788
- import_path24 = require("path");
15869
+ import_fs23 = require("fs");
15870
+ import_path25 = require("path");
15789
15871
  SIGNAL_DIRS = [
15790
15872
  "src",
15791
15873
  "pages",
@@ -15817,16 +15899,16 @@ var init_project_initializer = __esm({
15817
15899
  this.config = config2;
15818
15900
  }
15819
15901
  scanDirectory(root) {
15820
- const absRoot = (0, import_path24.resolve)(root);
15902
+ const absRoot = (0, import_path25.resolve)(root);
15821
15903
  const signals = { dependencies: [], directories: [], files: [] };
15822
15904
  for (const [file2, lang] of Object.entries(LANG_FILES)) {
15823
- if (this.safeExists(absRoot, (0, import_path24.join)(absRoot, file2))) signals.language = lang;
15905
+ if (this.safeExists(absRoot, (0, import_path25.join)(absRoot, file2))) signals.language = lang;
15824
15906
  }
15825
- const pkgPath = (0, import_path24.join)(absRoot, "package.json");
15907
+ const pkgPath = (0, import_path25.join)(absRoot, "package.json");
15826
15908
  if (this.safeExists(absRoot, pkgPath)) {
15827
15909
  signals.files.push("package.json");
15828
15910
  try {
15829
- const pkg = JSON.parse((0, import_fs22.readFileSync)(pkgPath, "utf-8"));
15911
+ const pkg = JSON.parse((0, import_fs23.readFileSync)(pkgPath, "utf-8"));
15830
15912
  signals.dependencies = [
15831
15913
  ...Object.keys(pkg.dependencies || {}),
15832
15914
  ...Object.keys(pkg.devDependencies || {})
@@ -15836,18 +15918,18 @@ var init_project_initializer = __esm({
15836
15918
  if (!signals.language) signals.language = "JavaScript";
15837
15919
  }
15838
15920
  for (const dir of SIGNAL_DIRS) {
15839
- const p = (0, import_path24.join)(absRoot, dir);
15921
+ const p = (0, import_path25.join)(absRoot, dir);
15840
15922
  if (this.safeExists(absRoot, p) && this.isDir(p)) signals.directories.push(`${dir}/`);
15841
15923
  }
15842
- const wfPath = (0, import_path24.join)(absRoot, ".github", "workflows");
15924
+ const wfPath = (0, import_path25.join)(absRoot, ".github", "workflows");
15843
15925
  if (this.safeExists(absRoot, wfPath) && this.isDir(wfPath)) {
15844
15926
  signals.directories.push(".github/workflows/");
15845
15927
  }
15846
15928
  for (const file2 of SIGNAL_FILES) {
15847
- if (this.safeExists(absRoot, (0, import_path24.join)(absRoot, file2))) signals.files.push(file2);
15929
+ if (this.safeExists(absRoot, (0, import_path25.join)(absRoot, file2))) signals.files.push(file2);
15848
15930
  }
15849
15931
  for (const file2 of Object.keys(LANG_FILES)) {
15850
- if (this.safeExists(absRoot, (0, import_path24.join)(absRoot, file2))) signals.files.push(file2);
15932
+ if (this.safeExists(absRoot, (0, import_path25.join)(absRoot, file2))) signals.files.push(file2);
15851
15933
  }
15852
15934
  return signals;
15853
15935
  }
@@ -15865,7 +15947,8 @@ var init_project_initializer = __esm({
15865
15947
  for (const p of ["google", "anthropic", "openai"]) {
15866
15948
  if (await this.config.keyProvider(p)) providers.push(p);
15867
15949
  }
15868
- if (!providers.length) {
15950
+ const hostIsClaudeCode = process.env.CLAUDECODE === "1" || !!process.env.CLAUDE_CODE_ENTRYPOINT;
15951
+ if (!providers.length && !hostIsClaudeCode) {
15869
15952
  return { text: "No API keys available. Run gossipcat setup to configure providers." };
15870
15953
  }
15871
15954
  const catalog = new ArchetypeCatalog(this.config.catalogPath);
@@ -15877,11 +15960,14 @@ var init_project_initializer = __esm({
15877
15960
  anthropic: { best: "claude-opus-4-6", fast: "claude-sonnet-4-6", cheapest: "claude-haiku-4-5" },
15878
15961
  openai: { best: "gpt-4o", fast: "gpt-4o", cheapest: "gpt-4o-mini" }
15879
15962
  };
15880
- const availableModels = providers.map((p) => {
15881
- const tiers = MODEL_TIERS[p];
15882
- if (!tiers) return `${p}: (use any available model)`;
15883
- return `${p}: ${tiers.best} (best), ${tiers.fast} (fast), ${tiers.cheapest} (cheapest)`;
15884
- }).join("\n");
15963
+ const availableModels = [
15964
+ ...hostIsClaudeCode ? ["none: none (native Claude Code orchestration \u2014 no API key needed, preferred for main_agent on this host)"] : [],
15965
+ ...providers.map((p) => {
15966
+ const tiers = MODEL_TIERS[p];
15967
+ if (!tiers) return `${p}: (use any available model)`;
15968
+ return `${p}: ${tiers.best} (best), ${tiers.fast} (fast), ${tiers.cheapest} (cheapest)`;
15969
+ })
15970
+ ].join("\n");
15885
15971
  const brainstormCtx = signals.brainstormContext;
15886
15972
  const systemPrompt = `You are configuring an agent team for a software project.
15887
15973
 
@@ -15928,7 +16014,7 @@ You may add additional skills from the table above based on project needs. Do NO
15928
16014
  - Pick the best archetype and customize roles for this specific project
15929
16015
  - Use ONLY the exact model names listed above
15930
16016
  - Choose models based on project complexity: simple \u2192 "fast" for all, complex \u2192 "best" for critical roles
15931
- - For the main_agent (orchestrator), use the "best" model from the primary provider
16017
+ - For the main_agent (orchestrator): ${hostIsClaudeCode ? 'PREFER { "provider": "none", "model": "none" } \u2014 native Claude Code orchestration needs no API key and is the zero-config default on this host. Only pick a keyed provider if the user explicitly asks for one.' : 'use the "best" model from the primary provider'}
15932
16018
  - Do NOT include agent IDs \u2014 the system generates them automatically
15933
16019
  - **Scale team size to project complexity.** Simple (single-page app, script, simple game) \u2192 1-2 agents. Medium \u2192 2-3. Complex multi-module \u2192 4-5. NEVER duplicate roles.
15934
16020
  - Max 5 agents, prefer fewer. Every agent costs money.
@@ -15979,13 +16065,13 @@ ${agentList}`,
15979
16065
  }
15980
16066
  async writeConfig(projectRoot) {
15981
16067
  if (!this.pendingProposal) throw new Error("No pending proposal to write");
15982
- const gossipDir2 = (0, import_path24.join)(projectRoot, ".gossip");
15983
- if (!(0, import_fs22.existsSync)(gossipDir2)) (0, import_fs22.mkdirSync)(gossipDir2, { recursive: true });
16068
+ const gossipDir2 = (0, import_path25.join)(projectRoot, ".gossip");
16069
+ if (!(0, import_fs23.existsSync)(gossipDir2)) (0, import_fs23.mkdirSync)(gossipDir2, { recursive: true });
15984
16070
  const agents = {};
15985
16071
  for (const a of this.pendingProposal.agents || []) {
15986
16072
  agents[a.id] = { provider: a.provider, model: a.model, preset: a.preset, skills: a.skills || [] };
15987
- const agentDir = (0, import_path24.join)(gossipDir2, "agents", a.id);
15988
- if (!(0, import_fs22.existsSync)(agentDir)) (0, import_fs22.mkdirSync)(agentDir, { recursive: true });
16073
+ const agentDir = (0, import_path25.join)(gossipDir2, "agents", a.id);
16074
+ if (!(0, import_fs23.existsSync)(agentDir)) (0, import_fs23.mkdirSync)(agentDir, { recursive: true });
15989
16075
  }
15990
16076
  const config2 = {
15991
16077
  main_agent: this.pendingProposal.main_agent,
@@ -15996,20 +16082,20 @@ ${agentList}`,
15996
16082
  },
15997
16083
  agents
15998
16084
  };
15999
- (0, import_fs22.writeFileSync)((0, import_path24.join)(gossipDir2, "config.json"), JSON.stringify(config2, null, 2));
16085
+ (0, import_fs23.writeFileSync)((0, import_path25.join)(gossipDir2, "config.json"), JSON.stringify(config2, null, 2));
16000
16086
  }
16001
16087
  safeExists(root, target) {
16002
- const resolved = (0, import_path24.resolve)(target);
16088
+ const resolved = (0, import_path25.resolve)(target);
16003
16089
  if (!resolved.startsWith(root)) return false;
16004
16090
  try {
16005
- return !(0, import_fs22.lstatSync)(resolved).isSymbolicLink();
16091
+ return !(0, import_fs23.lstatSync)(resolved).isSymbolicLink();
16006
16092
  } catch {
16007
16093
  return false;
16008
16094
  }
16009
16095
  }
16010
16096
  isDir(target) {
16011
16097
  try {
16012
- return (0, import_fs22.lstatSync)(target).isDirectory();
16098
+ return (0, import_fs23.lstatSync)(target).isDirectory();
16013
16099
  } catch {
16014
16100
  return false;
16015
16101
  }
@@ -16019,12 +16105,12 @@ ${agentList}`,
16019
16105
  });
16020
16106
 
16021
16107
  // packages/orchestrator/src/team-manager.ts
16022
- var import_fs23, import_path25, TeamManager;
16108
+ var import_fs24, import_path26, TeamManager;
16023
16109
  var init_team_manager = __esm({
16024
16110
  "packages/orchestrator/src/team-manager.ts"() {
16025
16111
  "use strict";
16026
- import_fs23 = require("fs");
16027
- import_path25 = require("path");
16112
+ import_fs24 = require("fs");
16113
+ import_path26 = require("path");
16028
16114
  TeamManager = class {
16029
16115
  registry;
16030
16116
  pipeline;
@@ -16080,8 +16166,8 @@ var init_team_manager = __esm({
16080
16166
  applyAdd(config2) {
16081
16167
  this.registry.register(config2);
16082
16168
  this.writeConfig();
16083
- const dir = (0, import_path25.join)(this.projectRoot, ".gossip", "agents", config2.id);
16084
- if (!(0, import_fs23.existsSync)(dir)) (0, import_fs23.mkdirSync)(dir, { recursive: true });
16169
+ const dir = (0, import_path26.join)(this.projectRoot, ".gossip", "agents", config2.id);
16170
+ if (!(0, import_fs24.existsSync)(dir)) (0, import_fs24.mkdirSync)(dir, { recursive: true });
16085
16171
  this.pendingAction = null;
16086
16172
  }
16087
16173
  applyRemove(agentId) {
@@ -16115,11 +16201,11 @@ var init_team_manager = __esm({
16115
16201
  };
16116
16202
  }
16117
16203
  writeConfig() {
16118
- const configPath = (0, import_path25.join)(this.projectRoot, ".gossip", "config.json");
16204
+ const configPath = (0, import_path26.join)(this.projectRoot, ".gossip", "config.json");
16119
16205
  let existing = {};
16120
- if ((0, import_fs23.existsSync)(configPath)) {
16206
+ if ((0, import_fs24.existsSync)(configPath)) {
16121
16207
  try {
16122
- existing = JSON.parse((0, import_fs23.readFileSync)(configPath, "utf-8"));
16208
+ existing = JSON.parse((0, import_fs24.readFileSync)(configPath, "utf-8"));
16123
16209
  } catch {
16124
16210
  }
16125
16211
  }
@@ -16131,9 +16217,9 @@ var init_team_manager = __esm({
16131
16217
  ...a.preset ? { preset: a.preset } : {},
16132
16218
  skills: a.skills
16133
16219
  }));
16134
- const dir = (0, import_path25.join)(this.projectRoot, ".gossip");
16135
- if (!(0, import_fs23.existsSync)(dir)) (0, import_fs23.mkdirSync)(dir, { recursive: true });
16136
- (0, import_fs23.writeFileSync)(configPath, JSON.stringify(existing, null, 2) + "\n");
16220
+ const dir = (0, import_path26.join)(this.projectRoot, ".gossip");
16221
+ if (!(0, import_fs24.existsSync)(dir)) (0, import_fs24.mkdirSync)(dir, { recursive: true });
16222
+ (0, import_fs24.writeFileSync)(configPath, JSON.stringify(existing, null, 2) + "\n");
16137
16223
  }
16138
16224
  };
16139
16225
  }
@@ -16355,8 +16441,8 @@ message: Your question?
16355
16441
  }
16356
16442
  /** Start all worker agents (connect to relay) */
16357
16443
  async start() {
16358
- const { existsSync: existsSync41, readFileSync: readFileSync40 } = await import("fs");
16359
- const { join: join48 } = await import("path");
16444
+ const { existsSync: existsSync44, readFileSync: readFileSync41 } = await import("fs");
16445
+ const { join: join49 } = await import("path");
16360
16446
  for (const config2 of this.registry.getAll()) {
16361
16447
  if (config2.native) continue;
16362
16448
  if (this.workers.has(config2.id)) continue;
@@ -16365,8 +16451,8 @@ message: Your question?
16365
16451
  apiKey = await this.keyProviderFn(config2.provider) ?? void 0;
16366
16452
  }
16367
16453
  const llm = createProvider(config2.provider, config2.model, apiKey);
16368
- const instructionsPath = join48(this.projectRoot, ".gossip", "agents", config2.id, "instructions.md");
16369
- const instructions = existsSync41(instructionsPath) ? readFileSync40(instructionsPath, "utf-8") : void 0;
16454
+ const instructionsPath = join49(this.projectRoot, ".gossip", "agents", config2.id, "instructions.md");
16455
+ const instructions = existsSync44(instructionsPath) ? readFileSync41(instructionsPath, "utf-8") : void 0;
16370
16456
  const enableWebSearch = config2.preset === "researcher" || config2.skills.includes("research");
16371
16457
  const worker = new WorkerAgent(config2.id, llm, this.relayUrl, ALL_TOOLS, instructions, enableWebSearch, this.relayApiKey);
16372
16458
  await worker.start();
@@ -16548,16 +16634,16 @@ message: Your question?
16548
16634
  this.registry.register(config2);
16549
16635
  }
16550
16636
  async syncWorkers(keyProvider) {
16551
- const { existsSync: existsSync41, readFileSync: readFileSync40 } = await import("fs");
16552
- const { join: join48 } = await import("path");
16637
+ const { existsSync: existsSync44, readFileSync: readFileSync41 } = await import("fs");
16638
+ const { join: join49 } = await import("path");
16553
16639
  let added = 0;
16554
16640
  for (const ac of this.registry.getAll()) {
16555
16641
  if (ac.native) continue;
16556
16642
  if (this.workers.has(ac.id)) continue;
16557
16643
  const key = await keyProvider(ac.provider);
16558
16644
  const llm = createProvider(ac.provider, ac.model, key ?? void 0, void 0, ac.base_url);
16559
- const instructionsPath = join48(this.projectRoot, ".gossip", "agents", ac.id, "instructions.md");
16560
- const instructions = existsSync41(instructionsPath) ? readFileSync40(instructionsPath, "utf-8") : void 0;
16645
+ const instructionsPath = join49(this.projectRoot, ".gossip", "agents", ac.id, "instructions.md");
16646
+ const instructions = existsSync44(instructionsPath) ? readFileSync41(instructionsPath, "utf-8") : void 0;
16561
16647
  const enableWebSearch = ac.preset === "researcher" || ac.skills.includes("research");
16562
16648
  const worker = new WorkerAgent(ac.id, llm, this.relayUrl, ALL_TOOLS, instructions, enableWebSearch, this.relayApiKey);
16563
16649
  await worker.start();
@@ -17078,12 +17164,12 @@ var init_consensus_types = __esm({
17078
17164
  });
17079
17165
 
17080
17166
  // packages/orchestrator/src/skill-index.ts
17081
- var import_fs24, import_path26, DANGEROUS_KEYS, SkillIndex;
17167
+ var import_fs25, import_path27, DANGEROUS_KEYS, SkillIndex;
17082
17168
  var init_skill_index = __esm({
17083
17169
  "packages/orchestrator/src/skill-index.ts"() {
17084
17170
  "use strict";
17085
- import_fs24 = require("fs");
17086
- import_path26 = require("path");
17171
+ import_fs25 = require("fs");
17172
+ import_path27 = require("path");
17087
17173
  init_skill_name();
17088
17174
  DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype", "_project"]);
17089
17175
  SkillIndex = class {
@@ -17092,7 +17178,7 @@ var init_skill_index = __esm({
17092
17178
  dirty = false;
17093
17179
  _exists = false;
17094
17180
  constructor(projectRoot) {
17095
- this.filePath = (0, import_path26.join)(projectRoot, ".gossip", "skill-index.json");
17181
+ this.filePath = (0, import_path27.join)(projectRoot, ".gossip", "skill-index.json");
17096
17182
  this.load();
17097
17183
  }
17098
17184
  /** Bind a skill to an agent (creates or updates the slot) */
@@ -17284,7 +17370,7 @@ var init_skill_index = __esm({
17284
17370
  }
17285
17371
  load() {
17286
17372
  try {
17287
- const raw = (0, import_fs24.readFileSync)(this.filePath, "utf-8");
17373
+ const raw = (0, import_fs25.readFileSync)(this.filePath, "utf-8");
17288
17374
  const parsed = JSON.parse(raw);
17289
17375
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
17290
17376
  for (const key of Object.keys(parsed)) {
@@ -17306,9 +17392,9 @@ var init_skill_index = __esm({
17306
17392
  }
17307
17393
  save() {
17308
17394
  if (!this.dirty) return;
17309
- const dir = (0, import_path26.dirname)(this.filePath);
17310
- (0, import_fs24.mkdirSync)(dir, { recursive: true });
17311
- (0, import_fs24.writeFileSync)(this.filePath, JSON.stringify(this.data, null, 2) + "\n");
17395
+ const dir = (0, import_path27.dirname)(this.filePath);
17396
+ (0, import_fs25.mkdirSync)(dir, { recursive: true });
17397
+ (0, import_fs25.writeFileSync)(this.filePath, JSON.stringify(this.data, null, 2) + "\n");
17312
17398
  this._exists = true;
17313
17399
  this.dirty = false;
17314
17400
  }
@@ -17323,12 +17409,12 @@ function safeId(value) {
17323
17409
  }
17324
17410
  return encodeURIComponent(value);
17325
17411
  }
17326
- var import_fs25, import_path27, TaskGraphSync;
17412
+ var import_fs26, import_path28, TaskGraphSync;
17327
17413
  var init_task_graph_sync = __esm({
17328
17414
  "packages/orchestrator/src/task-graph-sync.ts"() {
17329
17415
  "use strict";
17330
- import_fs25 = require("fs");
17331
- import_path27 = require("path");
17416
+ import_fs26 = require("fs");
17417
+ import_path28 = require("path");
17332
17418
  TaskGraphSync = class {
17333
17419
  constructor(graph, supabaseUrl, supabaseKey, userId, projectId, projectRoot, displayName, migration) {
17334
17420
  this.graph = graph;
@@ -17338,7 +17424,7 @@ var init_task_graph_sync = __esm({
17338
17424
  this.projectId = projectId;
17339
17425
  this.displayName = displayName;
17340
17426
  this.migration = migration;
17341
- this.gossipDir = (0, import_path27.join)(projectRoot, ".gossip");
17427
+ this.gossipDir = (0, import_path28.join)(projectRoot, ".gossip");
17342
17428
  }
17343
17429
  gossipDir;
17344
17430
  migrationDone = false;
@@ -17461,9 +17547,9 @@ var init_task_graph_sync = __esm({
17461
17547
  });
17462
17548
  }
17463
17549
  async syncAgentScores() {
17464
- const perfPath = (0, import_path27.join)(this.gossipDir, "agent-performance.jsonl");
17465
- if (!(0, import_fs25.existsSync)(perfPath)) return 0;
17466
- const content = (0, import_fs25.readFileSync)(perfPath, "utf-8");
17550
+ const perfPath = (0, import_path28.join)(this.gossipDir, "agent-performance.jsonl");
17551
+ if (!(0, import_fs26.existsSync)(perfPath)) return 0;
17552
+ const content = (0, import_fs26.readFileSync)(perfPath, "utf-8");
17467
17553
  const lines = content.trim().split("\n").filter(Boolean);
17468
17554
  const meta3 = this.graph.getSyncMeta();
17469
17555
  let synced = 0;
@@ -17610,61 +17696,61 @@ Return JSON: { "<agentId>": "<summary>", ... }`
17610
17696
  // packages/orchestrator/src/rules-loader.ts
17611
17697
  function findBundledRules() {
17612
17698
  const candidates = [
17613
- (0, import_path28.resolve)(__dirname, "default-rules", "gossipcat-rules.md"),
17614
- (0, import_path28.resolve)(__dirname, "..", "default-rules", "gossipcat-rules.md"),
17615
- (0, import_path28.resolve)(process.cwd(), "packages", "orchestrator", "src", "default-rules", "gossipcat-rules.md")
17699
+ (0, import_path29.resolve)(__dirname, "default-rules", "gossipcat-rules.md"),
17700
+ (0, import_path29.resolve)(__dirname, "..", "default-rules", "gossipcat-rules.md"),
17701
+ (0, import_path29.resolve)(process.cwd(), "packages", "orchestrator", "src", "default-rules", "gossipcat-rules.md")
17616
17702
  ];
17617
17703
  for (const p of candidates) {
17618
- if ((0, import_fs26.existsSync)(p)) return p;
17704
+ if ((0, import_fs27.existsSync)(p)) return p;
17619
17705
  }
17620
17706
  return null;
17621
17707
  }
17622
17708
  function ensureRulesFile(projectRoot) {
17623
- const target = (0, import_path28.join)(projectRoot, ".gossip", "rules.md");
17624
- if ((0, import_fs26.existsSync)(target)) return { created: false, path: target };
17709
+ const target = (0, import_path29.join)(projectRoot, ".gossip", "rules.md");
17710
+ if ((0, import_fs27.existsSync)(target)) return { created: false, path: target };
17625
17711
  const bundled = findBundledRules();
17626
17712
  if (!bundled) return { created: false, path: null };
17627
17713
  try {
17628
- (0, import_fs26.mkdirSync)((0, import_path28.dirname)(target), { recursive: true });
17629
- (0, import_fs26.copyFileSync)(bundled, target);
17714
+ (0, import_fs27.mkdirSync)((0, import_path29.dirname)(target), { recursive: true });
17715
+ (0, import_fs27.copyFileSync)(bundled, target);
17630
17716
  return { created: true, path: target };
17631
17717
  } catch {
17632
17718
  return { created: false, path: null };
17633
17719
  }
17634
17720
  }
17635
17721
  function readRulesContent(projectRoot) {
17636
- const local = (0, import_path28.join)(projectRoot, ".gossip", "rules.md");
17637
- if ((0, import_fs26.existsSync)(local)) {
17722
+ const local = (0, import_path29.join)(projectRoot, ".gossip", "rules.md");
17723
+ if ((0, import_fs27.existsSync)(local)) {
17638
17724
  try {
17639
- return (0, import_fs26.readFileSync)(local, "utf-8");
17725
+ return (0, import_fs27.readFileSync)(local, "utf-8");
17640
17726
  } catch {
17641
17727
  }
17642
17728
  }
17643
17729
  const bundled = findBundledRules();
17644
17730
  if (bundled) {
17645
17731
  try {
17646
- return (0, import_fs26.readFileSync)(bundled, "utf-8");
17732
+ return (0, import_fs27.readFileSync)(bundled, "utf-8");
17647
17733
  } catch {
17648
17734
  }
17649
17735
  }
17650
17736
  return null;
17651
17737
  }
17652
- var import_fs26, import_path28;
17738
+ var import_fs27, import_path29;
17653
17739
  var init_rules_loader = __esm({
17654
17740
  "packages/orchestrator/src/rules-loader.ts"() {
17655
17741
  "use strict";
17656
- import_fs26 = require("fs");
17657
- import_path28 = require("path");
17742
+ import_fs27 = require("fs");
17743
+ import_path29 = require("path");
17658
17744
  }
17659
17745
  });
17660
17746
 
17661
17747
  // packages/orchestrator/src/bootstrap.ts
17662
- var import_fs27, import_path29, log6, BootstrapGenerator;
17748
+ var import_fs28, import_path30, log6, BootstrapGenerator;
17663
17749
  var init_bootstrap = __esm({
17664
17750
  "packages/orchestrator/src/bootstrap.ts"() {
17665
17751
  "use strict";
17666
- import_fs27 = require("fs");
17667
- import_path29 = require("path");
17752
+ import_fs28 = require("fs");
17753
+ import_path30 = require("path");
17668
17754
  init_rules_loader();
17669
17755
  log6 = (msg) => process.stderr.write(`[gossipcat] ${msg}
17670
17756
  `);
@@ -17687,23 +17773,23 @@ var init_bootstrap = __esm({
17687
17773
  };
17688
17774
  }
17689
17775
  migrateConfig() {
17690
- const oldPath = (0, import_path29.resolve)(this.projectRoot, "gossip.agents.json");
17691
- const newPath = (0, import_path29.resolve)(this.projectRoot, ".gossip", "config.json");
17692
- if (!(0, import_fs27.existsSync)(newPath) && (0, import_fs27.existsSync)(oldPath)) {
17693
- (0, import_fs27.mkdirSync)((0, import_path29.resolve)(this.projectRoot, ".gossip"), { recursive: true });
17694
- (0, import_fs27.copyFileSync)(oldPath, newPath);
17776
+ const oldPath = (0, import_path30.resolve)(this.projectRoot, "gossip.agents.json");
17777
+ const newPath = (0, import_path30.resolve)(this.projectRoot, ".gossip", "config.json");
17778
+ if (!(0, import_fs28.existsSync)(newPath) && (0, import_fs28.existsSync)(oldPath)) {
17779
+ (0, import_fs28.mkdirSync)((0, import_path30.resolve)(this.projectRoot, ".gossip"), { recursive: true });
17780
+ (0, import_fs28.copyFileSync)(oldPath, newPath);
17695
17781
  log6("Migrated config to .gossip/config.json \u2014 gossip.agents.json is now ignored.");
17696
17782
  }
17697
17783
  }
17698
17784
  loadConfig() {
17699
17785
  const paths = [
17700
- (0, import_path29.resolve)(this.projectRoot, ".gossip", "config.json"),
17701
- (0, import_path29.resolve)(this.projectRoot, "gossip.agents.json")
17786
+ (0, import_path30.resolve)(this.projectRoot, ".gossip", "config.json"),
17787
+ (0, import_path30.resolve)(this.projectRoot, "gossip.agents.json")
17702
17788
  ];
17703
17789
  for (const p of paths) {
17704
- if ((0, import_fs27.existsSync)(p)) {
17790
+ if ((0, import_fs28.existsSync)(p)) {
17705
17791
  try {
17706
- return JSON.parse((0, import_fs27.readFileSync)(p, "utf-8"));
17792
+ return JSON.parse((0, import_fs28.readFileSync)(p, "utf-8"));
17707
17793
  } catch {
17708
17794
  log6("Config parse error, falling back to setup mode");
17709
17795
  return null;
@@ -17724,9 +17810,9 @@ var init_bootstrap = __esm({
17724
17810
  skills: ac.skills || [],
17725
17811
  taskCount: 0
17726
17812
  };
17727
- const tasksPath = (0, import_path29.join)(this.projectRoot, ".gossip", "agents", id, "memory", "tasks.jsonl");
17728
- if ((0, import_fs27.existsSync)(tasksPath)) {
17729
- const lines = (0, import_fs27.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
17813
+ const tasksPath = (0, import_path30.join)(this.projectRoot, ".gossip", "agents", id, "memory", "tasks.jsonl");
17814
+ if ((0, import_fs28.existsSync)(tasksPath)) {
17815
+ const lines = (0, import_fs28.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean);
17730
17816
  let count = 0;
17731
17817
  let lastTs = "";
17732
17818
  for (const line of lines) {
@@ -17740,9 +17826,9 @@ var init_bootstrap = __esm({
17740
17826
  summary.taskCount = count;
17741
17827
  if (lastTs) summary.lastActive = lastTs.split("T")[0];
17742
17828
  }
17743
- const memPath = (0, import_path29.join)(this.projectRoot, ".gossip", "agents", id, "memory", "MEMORY.md");
17744
- if ((0, import_fs27.existsSync)(memPath)) {
17745
- const content = (0, import_fs27.readFileSync)(memPath, "utf-8").slice(0, 500);
17829
+ const memPath = (0, import_path30.join)(this.projectRoot, ".gossip", "agents", id, "memory", "MEMORY.md");
17830
+ if ((0, import_fs28.existsSync)(memPath)) {
17831
+ const content = (0, import_fs28.readFileSync)(memPath, "utf-8").slice(0, 500);
17746
17832
  const knowledgeLines = content.match(/- \[([^\]]+)\]/g);
17747
17833
  if (knowledgeLines?.length) {
17748
17834
  summary.topics = knowledgeLines.map((l) => l.replace(/- \[([^\]]+)\].*/, "$1")).join(", ");
@@ -17975,13 +18061,13 @@ Skills are auto-injected from agent config. Project-wide skills in .gossip/skill
17975
18061
  * Returns the body content of the top knowledge files, capped at 2500 chars.
17976
18062
  */
17977
18063
  readProjectMemory() {
17978
- const knowledgeDir = (0, import_path29.join)(this.projectRoot, ".gossip", "agents", "_project", "memory", "knowledge");
17979
- if (!(0, import_fs27.existsSync)(knowledgeDir)) return null;
17980
- const files = (0, import_fs27.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md"));
18064
+ const knowledgeDir = (0, import_path30.join)(this.projectRoot, ".gossip", "agents", "_project", "memory", "knowledge");
18065
+ if (!(0, import_fs28.existsSync)(knowledgeDir)) return null;
18066
+ const files = (0, import_fs28.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md"));
17981
18067
  if (files.length === 0) return null;
17982
18068
  const scored = files.map((f) => {
17983
18069
  try {
17984
- const content = (0, import_fs27.readFileSync)((0, import_path29.join)(knowledgeDir, f), "utf-8");
18070
+ const content = (0, import_fs28.readFileSync)((0, import_path30.join)(knowledgeDir, f), "utf-8");
17985
18071
  const importance = parseFloat(content.match(/importance:\s*([\d.]+)/)?.[1] ?? "0.5");
17986
18072
  const isPinned = /pinned:\s*true/i.test(content);
17987
18073
  const tsPart = f.slice(0, 19);
@@ -18006,9 +18092,9 @@ Skills are auto-injected from agent config. Project-wide skills in .gossip/skill
18006
18092
  * Annotates TODO/remaining lines where the referenced tool actually exists.
18007
18093
  */
18008
18094
  verifyToolClaims(content) {
18009
- const mcpPath = (0, import_path29.join)(this.projectRoot, "apps", "cli", "src", "mcp-server-sdk.ts");
18010
- if (!(0, import_fs27.existsSync)(mcpPath)) return content;
18011
- const rawSource = (0, import_fs27.readFileSync)(mcpPath, "utf-8");
18095
+ const mcpPath = (0, import_path30.join)(this.projectRoot, "apps", "cli", "src", "mcp-server-sdk.ts");
18096
+ if (!(0, import_fs28.existsSync)(mcpPath)) return content;
18097
+ const rawSource = (0, import_fs28.readFileSync)(mcpPath, "utf-8");
18012
18098
  const source = rawSource.replace(/\/\/.*|\/\*[\s\S]*?\*\//g, "");
18013
18099
  const keywordRe = /TODO|remaining|deferred|needed|pending/i;
18014
18100
  const toolRe = /gossip_\w+/;
@@ -18026,10 +18112,10 @@ Skills are auto-injected from agent config. Project-wide skills in .gossip/skill
18026
18112
  }
18027
18113
  /** Read .gossip/next-session.md if it exists — user/orchestrator notes for the next session */
18028
18114
  readNextSessionNotes() {
18029
- const notesPath = (0, import_path29.join)(this.projectRoot, ".gossip", "next-session.md");
18030
- if (!(0, import_fs27.existsSync)(notesPath)) return null;
18115
+ const notesPath = (0, import_path30.join)(this.projectRoot, ".gossip", "next-session.md");
18116
+ if (!(0, import_fs28.existsSync)(notesPath)) return null;
18031
18117
  try {
18032
- const content = (0, import_fs27.readFileSync)(notesPath, "utf-8").trim();
18118
+ const content = (0, import_fs28.readFileSync)(notesPath, "utf-8").trim();
18033
18119
  if (content.length === 0) return null;
18034
18120
  return this.verifyToolClaims(content.slice(0, 3e3));
18035
18121
  } catch {
@@ -18243,13 +18329,13 @@ var init_check_effectiveness = __esm({
18243
18329
  });
18244
18330
 
18245
18331
  // packages/orchestrator/src/skill-engine.ts
18246
- var import_fs28, import_crypto9, import_path30, SAFE_NAME, KNOWN_CATEGORIES, CATEGORY_KEYWORDS, REQUIRED_SECTIONS, BUNDLED_TEMPLATE, SkillEngine;
18332
+ var import_fs29, import_crypto9, import_path31, SAFE_NAME, KNOWN_CATEGORIES, CATEGORY_KEYWORDS, REQUIRED_SECTIONS, BUNDLED_TEMPLATE, SkillEngine;
18247
18333
  var init_skill_engine = __esm({
18248
18334
  "packages/orchestrator/src/skill-engine.ts"() {
18249
18335
  "use strict";
18250
- import_fs28 = require("fs");
18336
+ import_fs29 = require("fs");
18251
18337
  import_crypto9 = require("crypto");
18252
- import_path30 = require("path");
18338
+ import_path31 = require("path");
18253
18339
  init_skill_name();
18254
18340
  init_check_effectiveness();
18255
18341
  SAFE_NAME = /^[a-z0-9][a-z0-9_-]{0,62}$/;
@@ -18329,7 +18415,13 @@ NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST.
18329
18415
  this.projectRoot = projectRoot;
18330
18416
  }
18331
18417
  techStackCache = void 0;
18332
- async generate(agentId, category) {
18418
+ /**
18419
+ * Build the LLM prompt strings and metadata needed to generate a skill file.
18420
+ * Separated from generate() so callers (e.g. the native-utility re-entry path)
18421
+ * can obtain the prompt, dispatch it externally, and later call saveFromRaw()
18422
+ * with the result — without re-running the expensive data-gathering step.
18423
+ */
18424
+ async buildPrompt(agentId, category) {
18333
18425
  if (!SAFE_NAME.test(agentId)) {
18334
18426
  throw new Error(`Invalid agent_id: "${agentId}". Must be lowercase alphanumeric with hyphens/underscores.`);
18335
18427
  }
@@ -18350,9 +18442,9 @@ NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST.
18350
18442
  }
18351
18443
  }
18352
18444
  let projectContext = "";
18353
- const bootstrapPath = (0, import_path30.join)(this.projectRoot, ".gossip", "bootstrap.md");
18354
- if ((0, import_fs28.existsSync)(bootstrapPath)) {
18355
- projectContext = (0, import_fs28.readFileSync)(bootstrapPath, "utf-8").slice(0, 1500);
18445
+ const bootstrapPath = (0, import_path31.join)(this.projectRoot, ".gossip", "bootstrap.md");
18446
+ if ((0, import_fs29.existsSync)(bootstrapPath)) {
18447
+ projectContext = (0, import_fs29.readFileSync)(bootstrapPath, "utf-8").slice(0, 1500);
18356
18448
  }
18357
18449
  if (this.techStackCache === void 0) {
18358
18450
  this.techStackCache = await this.detectTechStack();
@@ -18368,10 +18460,13 @@ ${techStack}
18368
18460
  const totalDispatches = agentScoreData?.totalSignals ?? 0;
18369
18461
  const categoryConfirmations = findings.filter((f) => f.agentId === agentId).length;
18370
18462
  const baselineRate = totalDispatches > 0 ? categoryConfirmations / totalDispatches : 0;
18371
- const messages = [
18372
- {
18373
- role: "system",
18374
- content: `You are a senior prompt engineer who builds skill files for AI code review agents. Your skills are injected into agent system prompts at dispatch time \u2014 every word costs tokens and shapes behavior. You write concise, opinionated methodology that changes how an agent thinks about a specific class of problems.
18463
+ const lifetime = this.perfReader.getCountersSince(agentId, category, 0);
18464
+ const baseline_accuracy_correct = lifetime.correct;
18465
+ const baseline_accuracy_hallucinated = lifetime.hallucinated;
18466
+ const bound_at = (/* @__PURE__ */ new Date()).toISOString();
18467
+ const skillName = normalizeSkillName(category);
18468
+ const skillPath = (0, import_path31.join)(this.projectRoot, ".gossip", "agents", agentId, "skills", `${skillName}.md`);
18469
+ const system = `You are a senior prompt engineer who builds skill files for AI code review agents. Your skills are injected into agent system prompts at dispatch time \u2014 every word costs tokens and shapes behavior. You write concise, opinionated methodology that changes how an agent thinks about a specific class of problems.
18375
18470
 
18376
18471
  Your output quality is measured by:
18377
18472
  1. **Relevance** \u2014 every check must apply to THIS project's tech stack. Generic checklists are waste.
@@ -18383,11 +18478,8 @@ Study this reference skill \u2014 it represents the quality bar:
18383
18478
 
18384
18479
  <reference_skill>
18385
18480
  ${template}
18386
- </reference_skill>`
18387
- },
18388
- {
18389
- role: "user",
18390
- content: `Generate a skill file for agent "${agentId}" to improve its "${category}" review performance.
18481
+ </reference_skill>`;
18482
+ const user = `Generate a skill file for agent "${agentId}" to improve its "${category}" review performance.
18391
18483
 
18392
18484
  <project_context>
18393
18485
  ${projectContext || "No project context available."}
@@ -18418,27 +18510,42 @@ Requirements:
18418
18510
  - Keep under 150 lines
18419
18511
  - CRITICAL: Tailor ALL content to the project's actual tech stack (see <tech_stack>). Only include checks relevant to technologies the project uses. If the project has no SQL database, do NOT mention SQL injection. If no HTML rendering, do NOT mention XSS. Generic security checklists waste agent prompt tokens.
18420
18512
  - Reference actual project file paths and patterns from findings and context
18421
- - Use bullet lists instead of markdown tables for Anti-Patterns (tables render poorly in agent prompts)`
18422
- }
18423
- ];
18424
- const response = await this.llm.generate(messages, { temperature: 0.3 });
18425
- const content = response.text || "";
18426
- let cleaned = content.trim();
18513
+ - Use bullet lists instead of markdown tables for Anti-Patterns (tables render poorly in agent prompts)`;
18514
+ return { system, user, skillName, skillPath, baseline_accuracy_correct, baseline_accuracy_hallucinated, bound_at };
18515
+ }
18516
+ /**
18517
+ * Persist a raw LLM-generated skill markdown string to disk.
18518
+ * Mirrors the post-LLM steps from generate(): code-fence stripping,
18519
+ * validation, snapshot-field injection, and atomic file write.
18520
+ *
18521
+ * Called by the native-utility re-entry path after gossip_relay delivers
18522
+ * the agent output back to the orchestrator.
18523
+ */
18524
+ saveFromRaw(_agentId, _category, rawMarkdown, meta3) {
18525
+ let cleaned = rawMarkdown.trim();
18427
18526
  if (cleaned.startsWith("```")) {
18428
18527
  cleaned = cleaned.replace(/^```\w*\n/, "").replace(/\n```\s*$/, "").trim();
18429
18528
  }
18430
18529
  this.validateSkillContent(cleaned);
18431
- const lifetime = this.perfReader.getCountersSince(agentId, category, 0);
18432
- const baseline_accuracy_correct = lifetime.correct;
18433
- const baseline_accuracy_hallucinated = lifetime.hallucinated;
18434
- const bound_at = (/* @__PURE__ */ new Date()).toISOString();
18435
- cleaned = this.injectSnapshotFields(cleaned, { baseline_accuracy_correct, baseline_accuracy_hallucinated, bound_at });
18436
- const skillName = normalizeSkillName(category);
18437
- const skillDir = (0, import_path30.join)(this.projectRoot, ".gossip", "agents", agentId, "skills");
18438
- (0, import_fs28.mkdirSync)(skillDir, { recursive: true });
18439
- const skillPath = (0, import_path30.join)(skillDir, `${skillName}.md`);
18440
- (0, import_fs28.writeFileSync)(skillPath, cleaned);
18441
- return { path: skillPath, content: cleaned };
18530
+ cleaned = this.injectSnapshotFields(cleaned, {
18531
+ baseline_accuracy_correct: meta3.baseline_accuracy_correct,
18532
+ baseline_accuracy_hallucinated: meta3.baseline_accuracy_hallucinated,
18533
+ bound_at: meta3.bound_at
18534
+ });
18535
+ const skillDir = (0, import_path31.join)(this.projectRoot, ".gossip", "agents", _agentId, "skills");
18536
+ (0, import_fs29.mkdirSync)(skillDir, { recursive: true });
18537
+ (0, import_fs29.writeFileSync)(meta3.skillPath, cleaned);
18538
+ return { path: meta3.skillPath, content: cleaned };
18539
+ }
18540
+ async generate(agentId, category) {
18541
+ const promptData = await this.buildPrompt(agentId, category);
18542
+ const messages = [
18543
+ { role: "system", content: promptData.system },
18544
+ { role: "user", content: promptData.user }
18545
+ ];
18546
+ const response = await this.llm.generate(messages, { temperature: 0.3 });
18547
+ const content = response.text || "";
18548
+ return this.saveFromRaw(agentId, category, content, promptData);
18442
18549
  }
18443
18550
  /**
18444
18551
  * Post-processes LLM-generated skill content to inject or overwrite snapshot fields
@@ -18481,24 +18588,24 @@ ${fm}
18481
18588
  }
18482
18589
  }
18483
18590
  loadTemplate() {
18484
- const userDir = (0, import_path30.join)(this.projectRoot, ".gossip", "skill-templates");
18485
- if ((0, import_fs28.existsSync)(userDir)) {
18486
- const files = (0, import_fs28.readdirSync)(userDir).filter((f) => f.endsWith(".md"));
18591
+ const userDir = (0, import_path31.join)(this.projectRoot, ".gossip", "skill-templates");
18592
+ if ((0, import_fs29.existsSync)(userDir)) {
18593
+ const files = (0, import_fs29.readdirSync)(userDir).filter((f) => f.endsWith(".md"));
18487
18594
  if (files.length > 0) {
18488
- return (0, import_fs28.readFileSync)((0, import_path30.join)(userDir, files[0]), "utf-8");
18595
+ return (0, import_fs29.readFileSync)((0, import_path31.join)(userDir, files[0]), "utf-8");
18489
18596
  }
18490
18597
  }
18491
18598
  const home = process.env.HOME || process.env.USERPROFILE || "";
18492
- const cacheBase = (0, import_path30.join)(home, ".claude", "plugins", "cache", "claude-plugins-official", "superpowers");
18493
- if ((0, import_fs28.existsSync)(cacheBase)) {
18599
+ const cacheBase = (0, import_path31.join)(home, ".claude", "plugins", "cache", "claude-plugins-official", "superpowers");
18600
+ if ((0, import_fs29.existsSync)(cacheBase)) {
18494
18601
  try {
18495
- const versions = (0, import_fs28.readdirSync)(cacheBase).sort().reverse();
18602
+ const versions = (0, import_fs29.readdirSync)(cacheBase).sort().reverse();
18496
18603
  for (const ver of versions) {
18497
- const skillPath = (0, import_path30.join)(cacheBase, ver, "skills", "systematic-debugging", "SKILL.md");
18498
- if ((0, import_fs28.existsSync)(skillPath)) {
18499
- const realPath = (0, import_fs28.realpathSync)(skillPath);
18500
- if (realPath.startsWith((0, import_path30.resolve)(cacheBase))) {
18501
- return (0, import_fs28.readFileSync)(realPath, "utf-8");
18604
+ const skillPath = (0, import_path31.join)(cacheBase, ver, "skills", "systematic-debugging", "SKILL.md");
18605
+ if ((0, import_fs29.existsSync)(skillPath)) {
18606
+ const realPath = (0, import_fs29.realpathSync)(skillPath);
18607
+ if (realPath.startsWith((0, import_path31.resolve)(cacheBase))) {
18608
+ return (0, import_fs29.readFileSync)(realPath, "utf-8");
18502
18609
  }
18503
18610
  }
18504
18611
  }
@@ -18514,20 +18621,20 @@ ${fm}
18514
18621
  */
18515
18622
  async detectTechStack() {
18516
18623
  const inputs = [];
18517
- const pkgPaths = [(0, import_path30.join)(this.projectRoot, "package.json")];
18624
+ const pkgPaths = [(0, import_path31.join)(this.projectRoot, "package.json")];
18518
18625
  try {
18519
- const packagesDir = (0, import_path30.join)(this.projectRoot, "packages");
18520
- if ((0, import_fs28.existsSync)(packagesDir)) {
18521
- for (const dir of (0, import_fs28.readdirSync)(packagesDir)) {
18522
- const p = (0, import_path30.join)(packagesDir, dir, "package.json");
18523
- if ((0, import_fs28.existsSync)(p)) pkgPaths.push(p);
18626
+ const packagesDir = (0, import_path31.join)(this.projectRoot, "packages");
18627
+ if ((0, import_fs29.existsSync)(packagesDir)) {
18628
+ for (const dir of (0, import_fs29.readdirSync)(packagesDir)) {
18629
+ const p = (0, import_path31.join)(packagesDir, dir, "package.json");
18630
+ if ((0, import_fs29.existsSync)(p)) pkgPaths.push(p);
18524
18631
  }
18525
18632
  }
18526
18633
  } catch {
18527
18634
  }
18528
18635
  for (const p of pkgPaths.slice(0, 5)) {
18529
18636
  try {
18530
- const pkg = JSON.parse((0, import_fs28.readFileSync)(p, "utf-8"));
18637
+ const pkg = JSON.parse((0, import_fs29.readFileSync)(p, "utf-8"));
18531
18638
  const deps = Object.keys({ ...pkg.dependencies, ...pkg.devDependencies });
18532
18639
  if (deps.length > 0) {
18533
18640
  inputs.push(`${p.replace(this.projectRoot + "/", "")}: ${deps.join(", ")}`);
@@ -18536,7 +18643,7 @@ ${fm}
18536
18643
  }
18537
18644
  }
18538
18645
  try {
18539
- const srcDirs = ["src", "packages", "apps", "lib"].filter((d) => (0, import_fs28.existsSync)((0, import_path30.join)(this.projectRoot, d)));
18646
+ const srcDirs = ["src", "packages", "apps", "lib"].filter((d) => (0, import_fs29.existsSync)((0, import_path31.join)(this.projectRoot, d)));
18540
18647
  inputs.push(`Source dirs: ${srcDirs.join(", ") || "root"}`);
18541
18648
  } catch {
18542
18649
  }
@@ -18563,10 +18670,10 @@ ${inputs.join("\n")}
18563
18670
  }
18564
18671
  }
18565
18672
  loadCategoryFindings(category) {
18566
- const filePath = (0, import_path30.join)(this.projectRoot, ".gossip", "agent-performance.jsonl");
18567
- if (!(0, import_fs28.existsSync)(filePath)) return [];
18673
+ const filePath = (0, import_path31.join)(this.projectRoot, ".gossip", "agent-performance.jsonl");
18674
+ if (!(0, import_fs29.existsSync)(filePath)) return [];
18568
18675
  try {
18569
- return (0, import_fs28.readFileSync)(filePath, "utf-8").trim().split("\n").filter(Boolean).map((line) => {
18676
+ return (0, import_fs29.readFileSync)(filePath, "utf-8").trim().split("\n").filter(Boolean).map((line) => {
18570
18677
  try {
18571
18678
  return JSON.parse(line);
18572
18679
  } catch {
@@ -18596,10 +18703,10 @@ ${inputs.join("\n")}
18596
18703
  return { status: "pending", shouldUpdate: false };
18597
18704
  }
18598
18705
  const skillPath = this.resolveSkillPath(agentId, category);
18599
- if (!(0, import_fs28.existsSync)(skillPath)) {
18706
+ if (!(0, import_fs29.existsSync)(skillPath)) {
18600
18707
  return { status: "pending", shouldUpdate: false };
18601
18708
  }
18602
- const raw = (0, import_fs28.readFileSync)(skillPath, "utf-8");
18709
+ const raw = (0, import_fs29.readFileSync)(skillPath, "utf-8");
18603
18710
  const { frontmatter: rawFrontmatter, body } = this.parseSkillFile(raw);
18604
18711
  const nowMs = Date.now();
18605
18712
  const { frontmatter, mutated } = this.migrateIfNeeded(
@@ -18677,7 +18784,7 @@ ${inputs.join("\n")}
18677
18784
  */
18678
18785
  resolveSkillPath(agentId, category) {
18679
18786
  const skillName = normalizeSkillName(category);
18680
- return (0, import_path30.join)(this.projectRoot, ".gossip", "agents", agentId, "skills", `${skillName}.md`);
18787
+ return (0, import_path31.join)(this.projectRoot, ".gossip", "agents", agentId, "skills", `${skillName}.md`);
18681
18788
  }
18682
18789
  /**
18683
18790
  * Splits a skill file into its frontmatter key-value map and the body text
@@ -18765,11 +18872,11 @@ ${fmLines.join("\n")}
18765
18872
  ---${body}`;
18766
18873
  const tmpPath = `${skillPath}.tmp.${process.pid}.${(0, import_crypto9.randomBytes)(4).toString("hex")}`;
18767
18874
  try {
18768
- (0, import_fs28.writeFileSync)(tmpPath, content, "utf-8");
18769
- (0, import_fs28.renameSync)(tmpPath, skillPath);
18875
+ (0, import_fs29.writeFileSync)(tmpPath, content, "utf-8");
18876
+ (0, import_fs29.renameSync)(tmpPath, skillPath);
18770
18877
  } catch (err) {
18771
18878
  try {
18772
- (0, import_fs28.unlinkSync)(tmpPath);
18879
+ (0, import_fs29.unlinkSync)(tmpPath);
18773
18880
  } catch {
18774
18881
  }
18775
18882
  throw err;
@@ -18780,12 +18887,12 @@ ${fmLines.join("\n")}
18780
18887
  });
18781
18888
 
18782
18889
  // packages/orchestrator/src/memory-searcher.ts
18783
- var import_fs29, import_path31, MAX_QUERY_LENGTH, MAX_KEYWORDS, MAX_TASK_FILE_BYTES, MemorySearcher;
18890
+ var import_fs30, import_path32, MAX_QUERY_LENGTH, MAX_KEYWORDS, MAX_TASK_FILE_BYTES, MemorySearcher;
18784
18891
  var init_memory_searcher = __esm({
18785
18892
  "packages/orchestrator/src/memory-searcher.ts"() {
18786
18893
  "use strict";
18787
- import_fs29 = require("fs");
18788
- import_path31 = require("path");
18894
+ import_fs30 = require("fs");
18895
+ import_path32 = require("path");
18789
18896
  MAX_QUERY_LENGTH = 500;
18790
18897
  MAX_KEYWORDS = 20;
18791
18898
  MAX_TASK_FILE_BYTES = 2 * 1024 * 1024;
@@ -18800,19 +18907,19 @@ var init_memory_searcher = __esm({
18800
18907
  const limit = Math.min(maxResults, 10);
18801
18908
  const keywords = this.extractKeywords(safeQuery);
18802
18909
  if (keywords.length === 0) return [];
18803
- const memDir = (0, import_path31.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
18804
- if (!(0, import_fs29.existsSync)(memDir)) return [];
18910
+ const memDir = (0, import_path32.join)(this.projectRoot, ".gossip", "agents", agentId, "memory");
18911
+ if (!(0, import_fs30.existsSync)(memDir)) return [];
18805
18912
  const results = [];
18806
- const knowledgeDir = (0, import_path31.join)(memDir, "knowledge");
18807
- if ((0, import_fs29.existsSync)(knowledgeDir)) {
18808
- const files = (0, import_fs29.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
18913
+ const knowledgeDir = (0, import_path32.join)(memDir, "knowledge");
18914
+ if ((0, import_fs30.existsSync)(knowledgeDir)) {
18915
+ const files = (0, import_fs30.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
18809
18916
  for (const file2 of files) {
18810
- const filePath = (0, import_path31.join)(knowledgeDir, file2);
18917
+ const filePath = (0, import_path32.join)(knowledgeDir, file2);
18811
18918
  try {
18812
- const content = (0, import_fs29.readFileSync)(filePath, "utf-8");
18919
+ const content = (0, import_fs30.readFileSync)(filePath, "utf-8");
18813
18920
  const frontmatter = this.parseFrontmatter(content);
18814
18921
  const body = content.replace(/^---[\s\S]*?---\n*/, "");
18815
- const name = frontmatter?.name || (0, import_path31.basename)(file2, ".md");
18922
+ const name = frontmatter?.name || (0, import_path32.basename)(file2, ".md");
18816
18923
  const description = frontmatter?.description || "";
18817
18924
  const importance = frontmatter?.importance ?? 0.5;
18818
18925
  const score = this.scoreContent(keywords, name, description, body, importance);
@@ -18829,12 +18936,12 @@ var init_memory_searcher = __esm({
18829
18936
  }
18830
18937
  }
18831
18938
  }
18832
- const tasksPath = (0, import_path31.join)(memDir, "tasks.jsonl");
18833
- if ((0, import_fs29.existsSync)(tasksPath)) {
18939
+ const tasksPath = (0, import_path32.join)(memDir, "tasks.jsonl");
18940
+ if ((0, import_fs30.existsSync)(tasksPath)) {
18834
18941
  try {
18835
- const stat3 = (0, import_fs29.statSync)(tasksPath);
18942
+ const stat3 = (0, import_fs30.statSync)(tasksPath);
18836
18943
  if (stat3.size > MAX_TASK_FILE_BYTES) return results.sort((a, b) => b.score - a.score).slice(0, limit);
18837
- const lines = (0, import_fs29.readFileSync)(tasksPath, "utf-8").split("\n").filter((l) => l.trim());
18944
+ const lines = (0, import_fs30.readFileSync)(tasksPath, "utf-8").split("\n").filter((l) => l.trim());
18838
18945
  for (const line of lines) {
18839
18946
  try {
18840
18947
  const entry = JSON.parse(line);
@@ -19120,9 +19227,9 @@ function prependScopeNote(prompt) {
19120
19227
  }
19121
19228
  function readSandboxMode(projectRoot) {
19122
19229
  try {
19123
- const p = (0, import_path32.join)(projectRoot, ".gossip", "config.json");
19124
- if (!(0, import_fs30.existsSync)(p)) return "warn";
19125
- const raw = JSON.parse((0, import_fs30.readFileSync)(p, "utf-8"));
19230
+ const p = (0, import_path33.join)(projectRoot, ".gossip", "config.json");
19231
+ if (!(0, import_fs31.existsSync)(p)) return "warn";
19232
+ const raw = JSON.parse((0, import_fs31.readFileSync)(p, "utf-8"));
19126
19233
  const mode = raw?.sandboxEnforcement;
19127
19234
  if (mode === "off" || mode === "warn" || mode === "block") return mode;
19128
19235
  return "warn";
@@ -19132,17 +19239,17 @@ function readSandboxMode(projectRoot) {
19132
19239
  }
19133
19240
  function recordDispatchMetadata(projectRoot, meta3) {
19134
19241
  try {
19135
- const dir = (0, import_path32.join)(projectRoot, ".gossip");
19136
- (0, import_fs30.mkdirSync)(dir, { recursive: true });
19137
- (0, import_fs30.appendFileSync)((0, import_path32.join)(dir, METADATA_FILE), JSON.stringify(meta3) + "\n");
19242
+ const dir = (0, import_path33.join)(projectRoot, ".gossip");
19243
+ (0, import_fs31.mkdirSync)(dir, { recursive: true });
19244
+ (0, import_fs31.appendFileSync)((0, import_path33.join)(dir, METADATA_FILE), JSON.stringify(meta3) + "\n");
19138
19245
  } catch {
19139
19246
  }
19140
19247
  }
19141
19248
  function lookupDispatchMetadata(projectRoot, taskId) {
19142
19249
  try {
19143
- const p = (0, import_path32.join)(projectRoot, ".gossip", METADATA_FILE);
19144
- if (!(0, import_fs30.existsSync)(p)) return null;
19145
- const raw = (0, import_fs30.readFileSync)(p, "utf-8");
19250
+ const p = (0, import_path33.join)(projectRoot, ".gossip", METADATA_FILE);
19251
+ if (!(0, import_fs31.existsSync)(p)) return null;
19252
+ const raw = (0, import_fs31.readFileSync)(p, "utf-8");
19146
19253
  const lines = raw.split("\n").filter(Boolean);
19147
19254
  for (let i = lines.length - 1; i >= 0; i--) {
19148
19255
  try {
@@ -19174,21 +19281,21 @@ function parseGitStatus(porcelain) {
19174
19281
  function normalizeScope(scope, projectRoot) {
19175
19282
  let s = scope.trim();
19176
19283
  if (!s) return "";
19177
- if ((0, import_path32.isAbsolute)(s)) {
19178
- s = (0, import_path32.relative)(projectRoot, s);
19284
+ if ((0, import_path33.isAbsolute)(s)) {
19285
+ s = (0, import_path33.relative)(projectRoot, s);
19179
19286
  } else if (s.startsWith("./")) {
19180
19287
  s = s.slice(2);
19181
19288
  }
19182
- s = (0, import_path32.normalize)(s).replace(/\/+$/, "");
19289
+ s = (0, import_path33.normalize)(s).replace(/\/+$/, "");
19183
19290
  if (s === "." || s === "") return "";
19184
19291
  return s;
19185
19292
  }
19186
19293
  function isInsideScope(filePath, scope) {
19187
19294
  if (!scope) return true;
19188
- const f = (0, import_path32.normalize)(filePath).replace(/^\.\//, "");
19295
+ const f = (0, import_path33.normalize)(filePath).replace(/^\.\//, "");
19189
19296
  const s = scope.replace(/^\.\//, "").replace(/\/+$/, "");
19190
19297
  if (f === s) return true;
19191
- return f.startsWith(s + "/") || f.startsWith(s + import_path32.sep);
19298
+ return f.startsWith(s + "/") || f.startsWith(s + import_path33.sep);
19192
19299
  }
19193
19300
  function detectBoundaryEscapes(meta3, modifiedFiles, projectRoot) {
19194
19301
  const mode = meta3.writeMode;
@@ -19250,8 +19357,8 @@ function recordBoundaryEscape(projectRoot, meta3, violations, mode) {
19250
19357
  } catch {
19251
19358
  }
19252
19359
  try {
19253
- const dir = (0, import_path32.join)(projectRoot, ".gossip");
19254
- (0, import_fs30.mkdirSync)(dir, { recursive: true });
19360
+ const dir = (0, import_path33.join)(projectRoot, ".gossip");
19361
+ (0, import_fs31.mkdirSync)(dir, { recursive: true });
19255
19362
  const line = {
19256
19363
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19257
19364
  taskId: meta3.taskId,
@@ -19262,17 +19369,17 @@ function recordBoundaryEscape(projectRoot, meta3, violations, mode) {
19262
19369
  action: mode
19263
19370
  // "warn" or "block"
19264
19371
  };
19265
- (0, import_fs30.appendFileSync)((0, import_path32.join)(dir, BOUNDARY_ESCAPE_FILE), JSON.stringify(line) + "\n");
19372
+ (0, import_fs31.appendFileSync)((0, import_path33.join)(dir, BOUNDARY_ESCAPE_FILE), JSON.stringify(line) + "\n");
19266
19373
  } catch {
19267
19374
  }
19268
19375
  }
19269
- var import_child_process4, import_fs30, import_path32, METADATA_FILE, BOUNDARY_ESCAPE_FILE, SYSTEM_PREFIXES, SCOPE_NOTE, __test__;
19376
+ var import_child_process4, import_fs31, import_path33, METADATA_FILE, BOUNDARY_ESCAPE_FILE, SYSTEM_PREFIXES, SCOPE_NOTE, __test__;
19270
19377
  var init_sandbox2 = __esm({
19271
19378
  "apps/cli/src/sandbox.ts"() {
19272
19379
  "use strict";
19273
19380
  import_child_process4 = require("child_process");
19274
- import_fs30 = require("fs");
19275
- import_path32 = require("path");
19381
+ import_fs31 = require("fs");
19382
+ import_path33 = require("path");
19276
19383
  METADATA_FILE = "dispatch-metadata.jsonl";
19277
19384
  BOUNDARY_ESCAPE_FILE = "boundary-escapes.jsonl";
19278
19385
  SYSTEM_PREFIXES = [
@@ -19356,11 +19463,11 @@ function startConsensusTimeout(consensusId) {
19356
19463
  const allEntries = [...snapshot.relayCrossReviewEntries, ...snapshot.nativeCrossReviewEntries];
19357
19464
  const report = await engine.synthesizeWithCrossReview(snapshot.allResults, allEntries, consensusId, snapshot.relayCrossReviewSkipped);
19358
19465
  try {
19359
- const { writeFileSync: writeFileSync17, mkdirSync: mkdirSync21 } = require("fs");
19360
- const { join: join48 } = require("path");
19361
- const reportsDir = join48(process.cwd(), ".gossip", "consensus-reports");
19362
- mkdirSync21(reportsDir, { recursive: true });
19363
- writeFileSync17(join48(reportsDir, `${consensusId}.json`), JSON.stringify({
19466
+ const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22 } = require("fs");
19467
+ const { join: join49 } = require("path");
19468
+ const reportsDir = join49(process.cwd(), ".gossip", "consensus-reports");
19469
+ mkdirSync22(reportsDir, { recursive: true });
19470
+ writeFileSync18(join49(reportsDir, `${consensusId}.json`), JSON.stringify({
19364
19471
  id: consensusId,
19365
19472
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19366
19473
  agentCount: report.agentCount,
@@ -19405,6 +19512,10 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
19405
19512
  round.pendingNativeAgents.delete(agent_id);
19406
19513
  process.stderr.write(`[gossipcat] \u{1F4E8} Cross-review received from ${agent_id}. Remaining: ${round.pendingNativeAgents.size}
19407
19514
  `);
19515
+ let parsedCount = 0;
19516
+ let acceptedCount = 0;
19517
+ const rejectedPeerIds = /* @__PURE__ */ new Set();
19518
+ let parseError = null;
19408
19519
  try {
19409
19520
  const { ConsensusEngine: ConsensusEngine2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
19410
19521
  const parseLlm = ctx.mainAgent.getLlm();
@@ -19414,14 +19525,51 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
19414
19525
  projectRoot: process.cwd()
19415
19526
  });
19416
19527
  const entries = engine.parseCrossReviewResponse(agent_id, result, 50);
19528
+ parsedCount = entries.length;
19417
19529
  const validPeerIds = new Set(round.allResults.map((r) => r.agentId));
19418
- const filtered = entries.filter((e) => e.peerAgentId !== agent_id && validPeerIds.has(e.peerAgentId));
19530
+ const filtered = entries.filter((e) => {
19531
+ const selfReview = e.peerAgentId === agent_id;
19532
+ const unknownPeer = !validPeerIds.has(e.peerAgentId);
19533
+ if (selfReview || unknownPeer) {
19534
+ if (e.peerAgentId) rejectedPeerIds.add(e.peerAgentId);
19535
+ return false;
19536
+ }
19537
+ return true;
19538
+ });
19539
+ acceptedCount = filtered.length;
19419
19540
  round.nativeCrossReviewEntries.push(...filtered);
19541
+ if (parsedCount > 0 && acceptedCount === 0) {
19542
+ process.stderr.write(
19543
+ `[gossipcat] \u26A0\uFE0F Cross-review from ${agent_id}: all ${parsedCount} entries rejected. Bad peer IDs: [${[...rejectedPeerIds].join(", ")}]. Valid round members: [${[...validPeerIds].join(", ")}]. Expected findingId format "<peerAgentId>:f<N>".
19544
+ `
19545
+ );
19546
+ } else if (parsedCount > acceptedCount) {
19547
+ process.stderr.write(
19548
+ `[gossipcat] \u26A0\uFE0F Cross-review from ${agent_id}: ${parsedCount - acceptedCount}/${parsedCount} entries rejected (bad peer IDs: [${[...rejectedPeerIds].join(", ")}]).
19549
+ `
19550
+ );
19551
+ }
19420
19552
  } catch (err) {
19421
- process.stderr.write(`[gossipcat] Failed to parse cross-review from ${agent_id}: ${err.message}
19553
+ parseError = err.message;
19554
+ process.stderr.write(`[gossipcat] Failed to parse cross-review from ${agent_id}: ${parseError}
19422
19555
  `);
19423
19556
  }
19424
19557
  persistPendingConsensus();
19558
+ const validPeerList = [...new Set(round.allResults.map((r) => r.agentId))].join(", ");
19559
+ let diagnostic = "";
19560
+ if (parseError) {
19561
+ diagnostic = `
19562
+ \u26A0\uFE0F Parse error: ${parseError}`;
19563
+ } else if (parsedCount > 0 && acceptedCount === 0) {
19564
+ diagnostic = `
19565
+ \u26A0\uFE0F All ${parsedCount} entries were REJECTED. Bad peer IDs: [${[...rejectedPeerIds].join(", ")}]. Valid round members: [${validPeerList}]. Expected findingId format "<peerAgentId>:f<N>" using exact agent IDs from the round.`;
19566
+ } else if (parsedCount > acceptedCount) {
19567
+ diagnostic = `
19568
+ \u26A0\uFE0F ${parsedCount - acceptedCount}/${parsedCount} entries rejected (bad peer IDs: [${[...rejectedPeerIds].join(", ")}]). Valid round members: [${validPeerList}].`;
19569
+ } else if (parsedCount > 0) {
19570
+ diagnostic = `
19571
+ \u2705 ${acceptedCount}/${parsedCount} entries accepted.`;
19572
+ }
19425
19573
  if (round.pendingNativeAgents.size > 0) {
19426
19574
  const EXTENSION_MS = 6e5;
19427
19575
  const MAX_TOTAL_MS = 36e5;
@@ -19430,7 +19578,7 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
19430
19578
  return {
19431
19579
  content: [{
19432
19580
  type: "text",
19433
- text: `Cross-review from ${agent_id} received. Waiting for ${round.pendingNativeAgents.size} more agent(s): ${[...round.pendingNativeAgents].join(", ")}`
19581
+ text: `Cross-review from ${agent_id} received. Waiting for ${round.pendingNativeAgents.size} more agent(s): ${[...round.pendingNativeAgents].join(", ")}${diagnostic}`
19434
19582
  }]
19435
19583
  };
19436
19584
  }
@@ -19467,12 +19615,12 @@ async function handleRelayCrossReview(consensus_id, agent_id, result) {
19467
19615
  synthSnapshot.relayCrossReviewSkipped
19468
19616
  );
19469
19617
  try {
19470
- const { writeFileSync: writeFileSync17, mkdirSync: mkdirSync21 } = require("fs");
19471
- const { join: join48 } = require("path");
19472
- const reportsDir = join48(process.cwd(), ".gossip", "consensus-reports");
19473
- mkdirSync21(reportsDir, { recursive: true });
19474
- const reportPath = join48(reportsDir, `${consensus_id}.json`);
19475
- writeFileSync17(reportPath, JSON.stringify({
19618
+ const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22 } = require("fs");
19619
+ const { join: join49 } = require("path");
19620
+ const reportsDir = join49(process.cwd(), ".gossip", "consensus-reports");
19621
+ mkdirSync22(reportsDir, { recursive: true });
19622
+ const reportPath = join49(reportsDir, `${consensus_id}.json`);
19623
+ writeFileSync18(reportPath, JSON.stringify({
19476
19624
  id: consensus_id,
19477
19625
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19478
19626
  agentCount: report.agentCount,
@@ -19512,10 +19660,10 @@ function persistPendingConsensus() {
19512
19660
  try {
19513
19661
  const projectRoot = ctx.mainAgent?.projectRoot;
19514
19662
  if (!projectRoot) return;
19515
- const { writeFileSync: writeFileSync17, mkdirSync: mkdirSync21 } = require("fs");
19516
- const { join: join48 } = require("path");
19517
- const dir = join48(projectRoot, ".gossip");
19518
- mkdirSync21(dir, { recursive: true });
19663
+ const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22 } = require("fs");
19664
+ const { join: join49 } = require("path");
19665
+ const dir = join49(projectRoot, ".gossip");
19666
+ mkdirSync22(dir, { recursive: true });
19519
19667
  const rounds = {};
19520
19668
  for (const [id, round] of ctx.pendingConsensusRounds) {
19521
19669
  rounds[id] = {
@@ -19537,7 +19685,7 @@ function persistPendingConsensus() {
19537
19685
  nativePrompts: (round.nativePrompts || []).filter((p) => round.pendingNativeAgents.has(p.agentId))
19538
19686
  };
19539
19687
  }
19540
- writeFileSync17(join48(dir, CONSENSUS_FILE), JSON.stringify(rounds));
19688
+ writeFileSync18(join49(dir, CONSENSUS_FILE), JSON.stringify(rounds));
19541
19689
  } catch (err) {
19542
19690
  process.stderr.write(`[gossipcat] persistPendingConsensus failed: ${err.message}
19543
19691
  `);
@@ -19545,11 +19693,11 @@ function persistPendingConsensus() {
19545
19693
  }
19546
19694
  function restorePendingConsensus(projectRoot) {
19547
19695
  try {
19548
- const { existsSync: existsSync41, readFileSync: readFileSync40, unlinkSync: unlinkSync4 } = require("fs");
19549
- const { join: join48 } = require("path");
19550
- const filePath = join48(projectRoot, ".gossip", CONSENSUS_FILE);
19551
- if (!existsSync41(filePath)) return;
19552
- const raw = JSON.parse(readFileSync40(filePath, "utf-8"));
19696
+ const { existsSync: existsSync44, readFileSync: readFileSync41, unlinkSync: unlinkSync4 } = require("fs");
19697
+ const { join: join49 } = require("path");
19698
+ const filePath = join49(projectRoot, ".gossip", CONSENSUS_FILE);
19699
+ if (!existsSync44(filePath)) return;
19700
+ const raw = JSON.parse(readFileSync41(filePath, "utf-8"));
19553
19701
  const now = Date.now();
19554
19702
  for (const [id, data] of Object.entries(raw)) {
19555
19703
  if (now > data.deadline + 3e5) {
@@ -19592,15 +19740,15 @@ __export(check_effectiveness_runner_exports, {
19592
19740
  runCheckEffectivenessForAllSkills: () => runCheckEffectivenessForAllSkills
19593
19741
  });
19594
19742
  async function runCheckEffectivenessForAllSkills(opts) {
19595
- const baseDir = (0, import_path34.join)(opts.projectRoot, ".gossip", "agents");
19596
- if (!(0, import_fs32.existsSync)(baseDir)) return;
19597
- const agentDirs = (0, import_fs32.readdirSync)(baseDir);
19743
+ const baseDir = (0, import_path35.join)(opts.projectRoot, ".gossip", "agents");
19744
+ if (!(0, import_fs33.existsSync)(baseDir)) return;
19745
+ const agentDirs = (0, import_fs33.readdirSync)(baseDir);
19598
19746
  for (const agentId of agentDirs) {
19599
- const skillsDir = (0, import_path34.join)(baseDir, agentId, "skills");
19600
- if (!(0, import_fs32.existsSync)(skillsDir)) continue;
19747
+ const skillsDir = (0, import_path35.join)(baseDir, agentId, "skills");
19748
+ if (!(0, import_fs33.existsSync)(skillsDir)) continue;
19601
19749
  const role = opts.registryGet(agentId)?.role;
19602
19750
  if (role === "implementer") continue;
19603
- const files = (0, import_fs32.readdirSync)(skillsDir).filter((f) => f.endsWith(".md"));
19751
+ const files = (0, import_fs33.readdirSync)(skillsDir).filter((f) => f.endsWith(".md"));
19604
19752
  for (const file2 of files) {
19605
19753
  const category = file2.replace(/\.md$/, "");
19606
19754
  try {
@@ -19621,12 +19769,12 @@ async function runCheckEffectivenessForAllSkills(opts) {
19621
19769
  }
19622
19770
  }
19623
19771
  }
19624
- var import_fs32, import_path34;
19772
+ var import_fs33, import_path35;
19625
19773
  var init_check_effectiveness_runner = __esm({
19626
19774
  "apps/cli/src/handlers/check-effectiveness-runner.ts"() {
19627
19775
  "use strict";
19628
- import_fs32 = require("fs");
19629
- import_path34 = require("path");
19776
+ import_fs33 = require("fs");
19777
+ import_path35 = require("path");
19630
19778
  }
19631
19779
  });
19632
19780
 
@@ -20309,12 +20457,12 @@ async function overviewHandler(projectRoot, ctx2) {
20309
20457
  const hourlyActivity = new Array(12).fill(0);
20310
20458
  const now = Date.now();
20311
20459
  const hourMs = 60 * 60 * 1e3;
20312
- const graphPath = (0, import_path35.join)(projectRoot, ".gossip", "task-graph.jsonl");
20313
- if ((0, import_fs33.existsSync)(graphPath)) {
20460
+ const graphPath = (0, import_path37.join)(projectRoot, ".gossip", "task-graph.jsonl");
20461
+ if ((0, import_fs35.existsSync)(graphPath)) {
20314
20462
  try {
20315
20463
  const created = /* @__PURE__ */ new Map();
20316
20464
  const finished = /* @__PURE__ */ new Set();
20317
- const lines = (0, import_fs33.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
20465
+ const lines = (0, import_fs35.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
20318
20466
  for (const line of lines) {
20319
20467
  try {
20320
20468
  const ev = JSON.parse(line);
@@ -20364,17 +20512,27 @@ async function overviewHandler(projectRoot, ctx2) {
20364
20512
  let confirmedFindings = 0;
20365
20513
  let lastConsensusTimestamp = "";
20366
20514
  let actionableFindings = 0;
20367
- const consensusTaskIds = /* @__PURE__ */ new Set();
20368
- const perfPath = (0, import_path35.join)(projectRoot, ".gossip", "agent-performance.jsonl");
20369
- if ((0, import_fs33.existsSync)(perfPath)) {
20515
+ const runBuckets = /* @__PURE__ */ new Map();
20516
+ const perfPath = (0, import_path37.join)(projectRoot, ".gossip", "agent-performance.jsonl");
20517
+ if ((0, import_fs35.existsSync)(perfPath)) {
20370
20518
  try {
20371
- const lines = (0, import_fs33.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
20519
+ const lines = (0, import_fs35.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
20372
20520
  for (const line of lines) {
20373
20521
  try {
20374
20522
  const entry = JSON.parse(line);
20375
- totalSignals++;
20523
+ if (entry.type === "consensus" && typeof entry.signal === "string") {
20524
+ totalSignals++;
20525
+ }
20376
20526
  if (entry.type === "consensus" && (entry.consensusId || entry.taskId)) {
20377
- consensusTaskIds.add(entry.consensusId ?? entry.taskId);
20527
+ const runId = entry.consensusId ?? entry.taskId;
20528
+ let bucket = runBuckets.get(runId);
20529
+ if (!bucket) {
20530
+ bucket = { agents: /* @__PURE__ */ new Set(), signalCount: 0 };
20531
+ runBuckets.set(runId, bucket);
20532
+ }
20533
+ bucket.signalCount++;
20534
+ if (entry.agentId) bucket.agents.add(entry.agentId);
20535
+ if (entry.counterpartId) bucket.agents.add(entry.counterpartId);
20378
20536
  }
20379
20537
  if (entry.consensusId && entry.timestamp > lastConsensusTimestamp) {
20380
20538
  lastConsensusTimestamp = entry.timestamp;
@@ -20397,27 +20555,29 @@ async function overviewHandler(projectRoot, ctx2) {
20397
20555
  } catch {
20398
20556
  }
20399
20557
  }
20400
- consensusRuns = consensusTaskIds.size;
20558
+ for (const bucket of runBuckets.values()) {
20559
+ if (bucket.agents.size >= 2 && bucket.signalCount >= 3) consensusRuns++;
20560
+ }
20401
20561
  const avgDurationMs = durationCount > 0 ? Math.round(totalDuration / durationCount) : 0;
20402
20562
  return { agentsOnline, relayCount, relayConnected, nativeCount, consensusRuns, totalFindings, confirmedFindings, totalSignals, tasksCompleted, tasksFailed, avgDurationMs, lastConsensusTimestamp, actionableFindings, hourlyActivity };
20403
20563
  }
20404
- var import_fs33, import_path35;
20564
+ var import_fs35, import_path37;
20405
20565
  var init_api_overview = __esm({
20406
20566
  "packages/relay/src/dashboard/api-overview.ts"() {
20407
20567
  "use strict";
20408
- import_fs33 = require("fs");
20409
- import_path35 = require("path");
20568
+ import_fs35 = require("fs");
20569
+ import_path37 = require("path");
20410
20570
  }
20411
20571
  });
20412
20572
 
20413
20573
  // packages/relay/src/dashboard/api-agents.ts
20414
20574
  function readTaskGraphByAgent(projectRoot) {
20415
- const taskGraphPath = (0, import_path36.join)(projectRoot, ".gossip", "task-graph.jsonl");
20575
+ const taskGraphPath = (0, import_path38.join)(projectRoot, ".gossip", "task-graph.jsonl");
20416
20576
  const result = /* @__PURE__ */ new Map();
20417
- if (!(0, import_fs34.existsSync)(taskGraphPath)) return result;
20577
+ if (!(0, import_fs36.existsSync)(taskGraphPath)) return result;
20418
20578
  let lines;
20419
20579
  try {
20420
- lines = (0, import_fs34.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
20580
+ lines = (0, import_fs36.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
20421
20581
  } catch {
20422
20582
  return result;
20423
20583
  }
@@ -20509,19 +20669,32 @@ async function agentsHandler(projectRoot, configs, onlineAgents = []) {
20509
20669
  hallucinations: score.hallucinations,
20510
20670
  consecutiveFailures: score.consecutiveFailures ?? 0,
20511
20671
  circuitOpen: score.circuitOpen ?? false,
20512
- categoryStrengths: score.categoryStrengths ?? {}
20672
+ // categoryStrengths is an UNBOUNDED severity-weighted accumulator used
20673
+ // for dispatch routing (severity × decay × 0.15 per confirmed signal),
20674
+ // not a [0,1] ratio. The dashboard must NOT render it as a percentage —
20675
+ // it should render categoryAccuracy (c / (c + h)) which is a real ratio.
20676
+ // See performance-reader.ts:357 (increment) vs :496-505 (accuracy).
20677
+ categoryStrengths: score.categoryStrengths ?? {},
20678
+ categoryAccuracy: score.categoryAccuracy ?? {},
20679
+ // Forward raw counts so the dashboard can render "100% (3/3)" and
20680
+ // distinguish a sparse-but-clean category from a high-volume clean
20681
+ // one. categoryAccuracy already drops categories with fewer than
20682
+ // MIN_CATEGORY_N signals, but the counts let the UI surface them
20683
+ // as dimmed "sparse" rows instead of hiding them silently.
20684
+ categoryCorrect: score.categoryCorrect ?? {},
20685
+ categoryHallucinated: score.categoryHallucinated ?? {}
20513
20686
  }
20514
20687
  };
20515
20688
  });
20516
20689
  }
20517
- var import_fs34, import_path36, DEFAULT_SCORE;
20690
+ var import_fs36, import_path38, DEFAULT_SCORE;
20518
20691
  var init_api_agents = __esm({
20519
20692
  "packages/relay/src/dashboard/api-agents.ts"() {
20520
20693
  "use strict";
20521
20694
  init_performance_reader();
20522
20695
  init_skill_index();
20523
- import_fs34 = require("fs");
20524
- import_path36 = require("path");
20696
+ import_fs36 = require("fs");
20697
+ import_path38 = require("path");
20525
20698
  DEFAULT_SCORE = {
20526
20699
  agentId: "",
20527
20700
  accuracy: 0.5,
@@ -20545,7 +20718,7 @@ var init_api_agents = __esm({
20545
20718
 
20546
20719
  // packages/relay/src/dashboard/api-skills.ts
20547
20720
  function isCorrupt(projectRoot, index) {
20548
- return (0, import_fs35.existsSync)((0, import_path37.join)(projectRoot, ".gossip", "skill-index.json")) && !index.exists();
20721
+ return (0, import_fs37.existsSync)((0, import_path39.join)(projectRoot, ".gossip", "skill-index.json")) && !index.exists();
20549
20722
  }
20550
20723
  async function skillsGetHandler(projectRoot) {
20551
20724
  try {
@@ -20574,13 +20747,13 @@ async function skillsBindHandler(projectRoot, body) {
20574
20747
  return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
20575
20748
  }
20576
20749
  }
20577
- var import_fs35, import_path37, AGENT_ID_RE2;
20750
+ var import_fs37, import_path39, AGENT_ID_RE2;
20578
20751
  var init_api_skills = __esm({
20579
20752
  "packages/relay/src/dashboard/api-skills.ts"() {
20580
20753
  "use strict";
20581
20754
  init_skill_index();
20582
- import_fs35 = require("fs");
20583
- import_path37 = require("path");
20755
+ import_fs37 = require("fs");
20756
+ import_path39 = require("path");
20584
20757
  AGENT_ID_RE2 = /^[a-zA-Z0-9_-]{1,64}$/;
20585
20758
  }
20586
20759
  });
@@ -20588,25 +20761,25 @@ var init_api_skills = __esm({
20588
20761
  // packages/relay/src/dashboard/api-memory.ts
20589
20762
  async function memoryHandler(projectRoot, agentId) {
20590
20763
  if (!agentId || !AGENT_ID_RE3.test(agentId) || DANGEROUS_IDS.has(agentId)) throw new Error("Invalid agent ID");
20591
- const memDir = (0, import_path38.join)(projectRoot, ".gossip", "agents", agentId, "memory");
20764
+ const memDir = (0, import_path40.join)(projectRoot, ".gossip", "agents", agentId, "memory");
20592
20765
  let index = "";
20593
- const indexPath = (0, import_path38.join)(memDir, "MEMORY.md");
20594
- if ((0, import_fs36.existsSync)(indexPath)) {
20766
+ const indexPath = (0, import_path40.join)(memDir, "MEMORY.md");
20767
+ if ((0, import_fs38.existsSync)(indexPath)) {
20595
20768
  try {
20596
- index = (0, import_fs36.readFileSync)(indexPath, "utf-8");
20769
+ index = (0, import_fs38.readFileSync)(indexPath, "utf-8");
20597
20770
  } catch {
20598
20771
  }
20599
20772
  }
20600
20773
  const knowledge = [];
20601
- const knowledgeDir = (0, import_path38.join)(memDir, "knowledge");
20774
+ const knowledgeDir = (0, import_path40.join)(memDir, "knowledge");
20602
20775
  const knowledgeDirs = [knowledgeDir, memDir];
20603
20776
  for (const dir of knowledgeDirs) {
20604
- if (!(0, import_fs36.existsSync)(dir)) continue;
20777
+ if (!(0, import_fs38.existsSync)(dir)) continue;
20605
20778
  try {
20606
- const files = (0, import_fs36.readdirSync)(dir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
20779
+ const files = (0, import_fs38.readdirSync)(dir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
20607
20780
  for (const filename of files) {
20608
20781
  try {
20609
- const raw = (0, import_fs36.readFileSync)((0, import_path38.join)(dir, filename), "utf-8");
20782
+ const raw = (0, import_fs38.readFileSync)((0, import_path40.join)(dir, filename), "utf-8");
20610
20783
  const { frontmatter, content } = parseFrontmatter(raw);
20611
20784
  knowledge.push({ filename, frontmatter, content });
20612
20785
  } catch {
@@ -20616,10 +20789,10 @@ async function memoryHandler(projectRoot, agentId) {
20616
20789
  }
20617
20790
  }
20618
20791
  const tasks = [];
20619
- const tasksPath = (0, import_path38.join)(memDir, "tasks.jsonl");
20620
- if ((0, import_fs36.existsSync)(tasksPath)) {
20792
+ const tasksPath = (0, import_path40.join)(memDir, "tasks.jsonl");
20793
+ if ((0, import_fs38.existsSync)(tasksPath)) {
20621
20794
  try {
20622
- const lines = (0, import_fs36.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean).slice(-200);
20795
+ const lines = (0, import_fs38.readFileSync)(tasksPath, "utf-8").trim().split("\n").filter(Boolean).slice(-200);
20623
20796
  for (const line of lines) {
20624
20797
  try {
20625
20798
  tasks.push(JSON.parse(line));
@@ -20647,12 +20820,12 @@ function parseFrontmatter(raw) {
20647
20820
  }
20648
20821
  return { frontmatter: fm, content: raw.slice(end + 3).trim() };
20649
20822
  }
20650
- var import_fs36, import_path38, AGENT_ID_RE3, DANGEROUS_IDS;
20823
+ var import_fs38, import_path40, AGENT_ID_RE3, DANGEROUS_IDS;
20651
20824
  var init_api_memory = __esm({
20652
20825
  "packages/relay/src/dashboard/api-memory.ts"() {
20653
20826
  "use strict";
20654
- import_fs36 = require("fs");
20655
- import_path38 = require("path");
20827
+ import_fs38 = require("fs");
20828
+ import_path40 = require("path");
20656
20829
  AGENT_ID_RE3 = /^[a-zA-Z0-9_-]{1,64}$/;
20657
20830
  DANGEROUS_IDS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
20658
20831
  }
@@ -20664,11 +20837,11 @@ async function consensusHandler(projectRoot, query) {
20664
20837
  const rawPageSize = parseInt(query?.get("pageSize") ?? "", 10);
20665
20838
  const page = isNaN(rawPage) || rawPage < 1 ? 1 : rawPage;
20666
20839
  const pageSize = isNaN(rawPageSize) || rawPageSize < 1 ? DEFAULT_PAGE_SIZE : Math.min(rawPageSize, MAX_PAGE_SIZE);
20667
- const perfPath = (0, import_path39.join)(projectRoot, ".gossip", "agent-performance.jsonl");
20668
- if (!(0, import_fs37.existsSync)(perfPath)) return { runs: [], totalRuns: 0, totalSignals: 0, page, pageSize };
20840
+ const perfPath = (0, import_path41.join)(projectRoot, ".gossip", "agent-performance.jsonl");
20841
+ if (!(0, import_fs39.existsSync)(perfPath)) return { runs: [], totalRuns: 0, totalSignals: 0, page, pageSize };
20669
20842
  const signals = [];
20670
20843
  try {
20671
- const lines = (0, import_fs37.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
20844
+ const lines = (0, import_fs39.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
20672
20845
  for (const line of lines) {
20673
20846
  try {
20674
20847
  const parsed = JSON.parse(line);
@@ -20739,12 +20912,12 @@ async function consensusHandler(projectRoot, query) {
20739
20912
  const paginatedRuns = runs.slice(offset, offset + pageSize);
20740
20913
  return { runs: paginatedRuns, totalRuns, totalSignals: signals.length, page, pageSize };
20741
20914
  }
20742
- var import_fs37, import_path39, RESOLUTION_SIGNALS, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE;
20915
+ var import_fs39, import_path41, RESOLUTION_SIGNALS, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE;
20743
20916
  var init_api_consensus = __esm({
20744
20917
  "packages/relay/src/dashboard/api-consensus.ts"() {
20745
20918
  "use strict";
20746
- import_fs37 = require("fs");
20747
- import_path39 = require("path");
20919
+ import_fs39 = require("fs");
20920
+ import_path41 = require("path");
20748
20921
  RESOLUTION_SIGNALS = /* @__PURE__ */ new Set(["agreement", "unique_confirmed", "consensus_verified"]);
20749
20922
  DEFAULT_PAGE_SIZE = 10;
20750
20923
  MAX_PAGE_SIZE = 50;
@@ -20756,11 +20929,11 @@ async function signalsHandler(projectRoot, query) {
20756
20929
  const agentFilter = query?.get("agent") ?? null;
20757
20930
  const limit = Math.min(Math.max(parseInt(query?.get("limit") ?? "", 10) || DEFAULT_LIMIT, 1), MAX_LIMIT);
20758
20931
  const offset = Math.max(parseInt(query?.get("offset") ?? "", 10) || 0, 0);
20759
- const perfPath = (0, import_path40.join)(projectRoot, ".gossip", "agent-performance.jsonl");
20760
- if (!(0, import_fs38.existsSync)(perfPath)) return { items: [], total: 0, offset, limit };
20932
+ const perfPath = (0, import_path42.join)(projectRoot, ".gossip", "agent-performance.jsonl");
20933
+ if (!(0, import_fs40.existsSync)(perfPath)) return { items: [], total: 0, offset, limit };
20761
20934
  const all = [];
20762
20935
  try {
20763
- const lines = (0, import_fs38.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
20936
+ const lines = (0, import_fs40.readFileSync)(perfPath, "utf-8").trim().split("\n").filter(Boolean);
20764
20937
  for (const line of lines) {
20765
20938
  try {
20766
20939
  const entry = JSON.parse(line);
@@ -20776,12 +20949,12 @@ async function signalsHandler(projectRoot, query) {
20776
20949
  all.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
20777
20950
  return { items: all.slice(offset, offset + limit), total: all.length, offset, limit };
20778
20951
  }
20779
- var import_fs38, import_path40, MAX_LIMIT, DEFAULT_LIMIT;
20952
+ var import_fs40, import_path42, MAX_LIMIT, DEFAULT_LIMIT;
20780
20953
  var init_api_signals = __esm({
20781
20954
  "packages/relay/src/dashboard/api-signals.ts"() {
20782
20955
  "use strict";
20783
- import_fs38 = require("fs");
20784
- import_path40 = require("path");
20956
+ import_fs40 = require("fs");
20957
+ import_path42 = require("path");
20785
20958
  MAX_LIMIT = 200;
20786
20959
  DEFAULT_LIMIT = 50;
20787
20960
  }
@@ -20789,29 +20962,29 @@ var init_api_signals = __esm({
20789
20962
 
20790
20963
  // packages/relay/src/dashboard/api-learnings.ts
20791
20964
  async function learningsHandler(projectRoot) {
20792
- const agentsDir = (0, import_path41.join)(projectRoot, ".gossip", "agents");
20793
- if (!(0, import_fs39.existsSync)(agentsDir)) return { learnings: [] };
20965
+ const agentsDir = (0, import_path43.join)(projectRoot, ".gossip", "agents");
20966
+ if (!(0, import_fs41.existsSync)(agentsDir)) return { learnings: [] };
20794
20967
  const all = [];
20795
20968
  let agentIds;
20796
20969
  try {
20797
- agentIds = (0, import_fs39.readdirSync)(agentsDir).filter((f) => !f.startsWith("."));
20970
+ agentIds = (0, import_fs41.readdirSync)(agentsDir).filter((f) => !f.startsWith("."));
20798
20971
  } catch {
20799
20972
  return { learnings: [] };
20800
20973
  }
20801
20974
  for (const agentId of agentIds) {
20802
- const knowledgeDir = (0, import_path41.join)(agentsDir, agentId, "memory", "knowledge");
20803
- if (!(0, import_fs39.existsSync)(knowledgeDir)) continue;
20975
+ const knowledgeDir = (0, import_path43.join)(agentsDir, agentId, "memory", "knowledge");
20976
+ if (!(0, import_fs41.existsSync)(knowledgeDir)) continue;
20804
20977
  let files;
20805
20978
  try {
20806
- files = (0, import_fs39.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
20979
+ files = (0, import_fs41.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md"));
20807
20980
  } catch {
20808
20981
  continue;
20809
20982
  }
20810
20983
  for (const filename of files) {
20811
- const filepath = (0, import_path41.join)(knowledgeDir, filename);
20984
+ const filepath = (0, import_path43.join)(knowledgeDir, filename);
20812
20985
  try {
20813
- const stat3 = (0, import_fs39.statSync)(filepath);
20814
- const raw = (0, import_fs39.readFileSync)(filepath, "utf-8");
20986
+ const stat3 = (0, import_fs41.statSync)(filepath);
20987
+ const raw = (0, import_fs41.readFileSync)(filepath, "utf-8");
20815
20988
  const fm = parseFrontmatter2(raw);
20816
20989
  all.push({
20817
20990
  agentId,
@@ -20838,12 +21011,12 @@ function parseFrontmatter2(raw) {
20838
21011
  }
20839
21012
  return fm;
20840
21013
  }
20841
- var import_fs39, import_path41, MAX_LEARNINGS;
21014
+ var import_fs41, import_path43, MAX_LEARNINGS;
20842
21015
  var init_api_learnings = __esm({
20843
21016
  "packages/relay/src/dashboard/api-learnings.ts"() {
20844
21017
  "use strict";
20845
- import_fs39 = require("fs");
20846
- import_path41 = require("path");
21018
+ import_fs41 = require("fs");
21019
+ import_path43 = require("path");
20847
21020
  MAX_LEARNINGS = 10;
20848
21021
  }
20849
21022
  });
@@ -20854,12 +21027,12 @@ async function tasksHandler(projectRoot, query) {
20854
21027
  const rawOffset = parseInt(query?.get("offset") ?? "0", 10);
20855
21028
  const limit = isNaN(rawLimit) || rawLimit < 1 ? 50 : Math.min(rawLimit, 200);
20856
21029
  const offset = isNaN(rawOffset) || rawOffset < 0 ? 0 : rawOffset;
20857
- const graphPath = (0, import_path42.join)(projectRoot, ".gossip", "task-graph.jsonl");
20858
- if (!(0, import_fs40.existsSync)(graphPath)) return { items: [], total: 0, offset, limit };
21030
+ const graphPath = (0, import_path44.join)(projectRoot, ".gossip", "task-graph.jsonl");
21031
+ if (!(0, import_fs42.existsSync)(graphPath)) return { items: [], total: 0, offset, limit };
20859
21032
  const created = /* @__PURE__ */ new Map();
20860
21033
  const completed = /* @__PURE__ */ new Map();
20861
21034
  try {
20862
- const lines = (0, import_fs40.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
21035
+ const lines = (0, import_fs42.readFileSync)(graphPath, "utf-8").trim().split("\n").filter(Boolean);
20863
21036
  for (const line of lines) {
20864
21037
  try {
20865
21038
  const entry = JSON.parse(line);
@@ -20914,23 +21087,23 @@ async function tasksHandler(projectRoot, query) {
20914
21087
  tasks.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
20915
21088
  return { items: tasks.slice(offset, offset + limit), total: tasks.length, offset, limit };
20916
21089
  }
20917
- var import_fs40, import_path42;
21090
+ var import_fs42, import_path44;
20918
21091
  var init_api_tasks = __esm({
20919
21092
  "packages/relay/src/dashboard/api-tasks.ts"() {
20920
21093
  "use strict";
20921
- import_fs40 = require("fs");
20922
- import_path42 = require("path");
21094
+ import_fs42 = require("fs");
21095
+ import_path44 = require("path");
20923
21096
  }
20924
21097
  });
20925
21098
 
20926
21099
  // packages/relay/src/dashboard/api-active-tasks.ts
20927
21100
  async function activeTasksHandler(projectRoot) {
20928
- const taskGraphPath = (0, import_path43.join)(projectRoot, ".gossip", "task-graph.jsonl");
20929
- if (!(0, import_fs41.existsSync)(taskGraphPath)) return { tasks: [] };
21101
+ const taskGraphPath = (0, import_path45.join)(projectRoot, ".gossip", "task-graph.jsonl");
21102
+ if (!(0, import_fs43.existsSync)(taskGraphPath)) return { tasks: [] };
20930
21103
  const created = /* @__PURE__ */ new Map();
20931
21104
  const finished = /* @__PURE__ */ new Set();
20932
21105
  try {
20933
- const lines = (0, import_fs41.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
21106
+ const lines = (0, import_fs43.readFileSync)(taskGraphPath, "utf-8").trim().split("\n").filter(Boolean);
20934
21107
  for (const line of lines) {
20935
21108
  try {
20936
21109
  const ev = JSON.parse(line);
@@ -20957,12 +21130,12 @@ async function activeTasksHandler(projectRoot) {
20957
21130
  active.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
20958
21131
  return { tasks: active.slice(0, 10) };
20959
21132
  }
20960
- var import_fs41, import_path43;
21133
+ var import_fs43, import_path45;
20961
21134
  var init_api_active_tasks = __esm({
20962
21135
  "packages/relay/src/dashboard/api-active-tasks.ts"() {
20963
21136
  "use strict";
20964
- import_fs41 = require("fs");
20965
- import_path43 = require("path");
21137
+ import_fs43 = require("fs");
21138
+ import_path45 = require("path");
20966
21139
  }
20967
21140
  });
20968
21141
 
@@ -20975,25 +21148,25 @@ function categorize(text) {
20975
21148
  return "other";
20976
21149
  }
20977
21150
  function logsHandler(projectRoot, query) {
20978
- const logPath = (0, import_path44.join)(projectRoot, ".gossip", "mcp.log");
20979
- if (!(0, import_fs42.existsSync)(logPath)) {
21151
+ const logPath = (0, import_path46.join)(projectRoot, ".gossip", "mcp.log");
21152
+ if (!(0, import_fs44.existsSync)(logPath)) {
20980
21153
  return { entries: [], totalLines: 0, fileSize: 0 };
20981
21154
  }
20982
21155
  const filter = query?.get("filter") || void 0;
20983
21156
  const tail = parseInt(query?.get("tail") || "200", 10);
20984
21157
  const clampedTail = Math.min(Math.max(tail, 10), 2e3);
20985
- const fileSize = (0, import_fs42.statSync)(logPath).size;
21158
+ const fileSize = (0, import_fs44.statSync)(logPath).size;
20986
21159
  const MAX_READ = 512 * 1024;
20987
21160
  const readFrom = Math.max(0, fileSize - MAX_READ);
20988
21161
  const readLen = fileSize - readFrom;
20989
- const fd = (0, import_fs42.openSync)(logPath, "r");
21162
+ const fd = (0, import_fs44.openSync)(logPath, "r");
20990
21163
  let buf = Buffer.alloc(0);
20991
21164
  try {
20992
21165
  buf = Buffer.allocUnsafe(readLen);
20993
- const bytesRead = (0, import_fs42.readSync)(fd, buf, 0, readLen, readFrom);
21166
+ const bytesRead = (0, import_fs44.readSync)(fd, buf, 0, readLen, readFrom);
20994
21167
  buf = buf.subarray(0, bytesRead);
20995
21168
  } finally {
20996
- (0, import_fs42.closeSync)(fd);
21169
+ (0, import_fs44.closeSync)(fd);
20997
21170
  }
20998
21171
  let raw = buf.toString("utf-8");
20999
21172
  let lineOffset = 0;
@@ -21001,13 +21174,13 @@ function logsHandler(projectRoot, query) {
21001
21174
  const nl = raw.indexOf("\n");
21002
21175
  raw = nl >= 0 ? raw.slice(nl + 1) : raw;
21003
21176
  const SCAN_CHUNK = 64 * 1024;
21004
- const scanFd = (0, import_fs42.openSync)(logPath, "r");
21177
+ const scanFd = (0, import_fs44.openSync)(logPath, "r");
21005
21178
  try {
21006
21179
  let pos = 0;
21007
21180
  const chunk = Buffer.allocUnsafe(SCAN_CHUNK);
21008
21181
  while (pos < readFrom) {
21009
21182
  const len = Math.min(SCAN_CHUNK, readFrom - pos);
21010
- const n = (0, import_fs42.readSync)(scanFd, chunk, 0, len, pos);
21183
+ const n = (0, import_fs44.readSync)(scanFd, chunk, 0, len, pos);
21011
21184
  if (n === 0) break;
21012
21185
  for (let j = 0; j < n; j++) {
21013
21186
  if (chunk[j] === 10) lineOffset++;
@@ -21015,7 +21188,7 @@ function logsHandler(projectRoot, query) {
21015
21188
  pos += n;
21016
21189
  }
21017
21190
  } finally {
21018
- (0, import_fs42.closeSync)(scanFd);
21191
+ (0, import_fs44.closeSync)(scanFd);
21019
21192
  }
21020
21193
  }
21021
21194
  const allLines = raw.split("\n").filter(Boolean);
@@ -21033,12 +21206,12 @@ function logsHandler(projectRoot, query) {
21033
21206
  entries = entries.slice(-clampedTail);
21034
21207
  return { entries, totalLines, fileSize, filter };
21035
21208
  }
21036
- var import_fs42, import_path44, CATEGORY_PATTERNS2;
21209
+ var import_fs44, import_path46, CATEGORY_PATTERNS2;
21037
21210
  var init_api_logs = __esm({
21038
21211
  "packages/relay/src/dashboard/api-logs.ts"() {
21039
21212
  "use strict";
21040
- import_fs42 = require("fs");
21041
- import_path44 = require("path");
21213
+ import_fs44 = require("fs");
21214
+ import_path46 = require("path");
21042
21215
  CATEGORY_PATTERNS2 = [
21043
21216
  [/^\[worker:/, "worker"],
21044
21217
  [/^\[Gemini\]/, "gemini"],
@@ -21066,8 +21239,22 @@ var init_api_logs = __esm({
21066
21239
  });
21067
21240
 
21068
21241
  // packages/relay/src/dashboard/routes.ts
21242
+ function resolveDashboardRoot(projectRoot) {
21243
+ const candidates = [
21244
+ (0, import_path47.resolve)(__dirname, "..", "dist-dashboard"),
21245
+ // bundled: dist-mcp/mcp-server.js → ../dist-dashboard
21246
+ (0, import_path47.resolve)(__dirname, "..", "..", "..", "..", "dist-dashboard"),
21247
+ // tsc dev: packages/relay/dist/dashboard → repo-root
21248
+ (0, import_path47.join)(projectRoot, "dist-dashboard")
21249
+ // legacy dev fallback (git-clone running from repo root)
21250
+ ];
21251
+ for (const p of candidates) {
21252
+ if ((0, import_fs45.existsSync)(p)) return p;
21253
+ }
21254
+ return null;
21255
+ }
21069
21256
  function readBody(req) {
21070
- return new Promise((resolve17, reject) => {
21257
+ return new Promise((resolve18, reject) => {
21071
21258
  const chunks = [];
21072
21259
  let size = 0;
21073
21260
  let tooLarge = false;
@@ -21082,14 +21269,14 @@ function readBody(req) {
21082
21269
  chunks.push(chunk);
21083
21270
  });
21084
21271
  req.on("end", () => {
21085
- if (!tooLarge) resolve17(Buffer.concat(chunks).toString("utf-8"));
21272
+ if (!tooLarge) resolve18(Buffer.concat(chunks).toString("utf-8"));
21086
21273
  });
21087
21274
  req.on("error", (err) => {
21088
21275
  if (!tooLarge) reject(err);
21089
21276
  });
21090
21277
  });
21091
21278
  }
21092
- var import_fs43, import_path45, AUTH_MAX_ATTEMPTS, AUTH_LOCKOUT_MS, DashboardRouter, MAX_BODY_SIZE;
21279
+ var import_fs45, import_path47, AUTH_MAX_ATTEMPTS, AUTH_LOCKOUT_MS, DashboardRouter, MAX_BODY_SIZE;
21093
21280
  var init_routes = __esm({
21094
21281
  "packages/relay/src/dashboard/routes.ts"() {
21095
21282
  "use strict";
@@ -21103,8 +21290,8 @@ var init_routes = __esm({
21103
21290
  init_api_tasks();
21104
21291
  init_api_active_tasks();
21105
21292
  init_api_logs();
21106
- import_fs43 = require("fs");
21107
- import_path45 = require("path");
21293
+ import_fs45 = require("fs");
21294
+ import_path47 = require("path");
21108
21295
  AUTH_MAX_ATTEMPTS = 10;
21109
21296
  AUTH_LOCKOUT_MS = 6e4;
21110
21297
  DashboardRouter = class {
@@ -21112,8 +21299,10 @@ var init_routes = __esm({
21112
21299
  this.auth = auth;
21113
21300
  this.projectRoot = projectRoot;
21114
21301
  this.ctx = ctx2;
21302
+ this.dashboardRoot = resolveDashboardRoot(projectRoot);
21115
21303
  }
21116
21304
  authAttempts = /* @__PURE__ */ new Map();
21305
+ dashboardRoot;
21117
21306
  /** Update live context (call when agents connect/disconnect) */
21118
21307
  updateContext(ctx2) {
21119
21308
  if (ctx2.agentConfigs !== void 0) this.ctx.agentConfigs = ctx2.agentConfigs;
@@ -21279,18 +21468,24 @@ var init_routes = __esm({
21279
21468
  return true;
21280
21469
  }
21281
21470
  serveDashboard(res) {
21282
- const htmlPath = (0, import_path45.join)(this.projectRoot, "dist-dashboard", "index.html");
21283
- if (!(0, import_fs43.existsSync)(htmlPath)) {
21471
+ if (!this.dashboardRoot) {
21284
21472
  res.writeHead(503, { "Content-Type": "text/plain" });
21285
- res.end("Dashboard not built. Run: npm run build:dashboard");
21473
+ res.end("Dashboard assets not found. Reinstall gossipcat or rebuild from source.");
21286
21474
  return true;
21287
21475
  }
21288
- const html = (0, import_fs43.readFileSync)(htmlPath, "utf-8");
21476
+ const htmlPath = (0, import_path47.join)(this.dashboardRoot, "index.html");
21477
+ if (!(0, import_fs45.existsSync)(htmlPath)) {
21478
+ res.writeHead(503, { "Content-Type": "text/plain" });
21479
+ res.end(`Dashboard index.html missing at ${this.dashboardRoot}. Reinstall gossipcat.`);
21480
+ return true;
21481
+ }
21482
+ const html = (0, import_fs45.readFileSync)(htmlPath, "utf-8");
21289
21483
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
21290
21484
  res.end(html);
21291
21485
  return true;
21292
21486
  }
21293
21487
  serveStaticFile(res, url2) {
21488
+ if (!this.dashboardRoot) return false;
21294
21489
  const relativePath = url2.replace(/^\/dashboard\//, "");
21295
21490
  if (relativePath.includes("..")) {
21296
21491
  res.writeHead(404);
@@ -21310,16 +21505,16 @@ var init_routes = __esm({
21310
21505
  const ext = "." + (relativePath.split(".").pop() || "");
21311
21506
  const mime = MIME[ext];
21312
21507
  if (!mime) return false;
21313
- const filePath = (0, import_path45.join)(this.projectRoot, "dist-dashboard", relativePath);
21508
+ const filePath = (0, import_path47.join)(this.dashboardRoot, relativePath);
21314
21509
  try {
21315
- const realFile = (0, import_fs43.realpathSync)(filePath);
21316
- const realBase = (0, import_fs43.realpathSync)((0, import_path45.resolve)(this.projectRoot, "dist-dashboard"));
21510
+ const realFile = (0, import_fs45.realpathSync)(filePath);
21511
+ const realBase = (0, import_fs45.realpathSync)(this.dashboardRoot);
21317
21512
  if (!realFile.startsWith(realBase + "/")) {
21318
21513
  res.writeHead(404);
21319
21514
  res.end();
21320
21515
  return true;
21321
21516
  }
21322
- const data = (0, import_fs43.readFileSync)(realFile);
21517
+ const data = (0, import_fs45.readFileSync)(realFile);
21323
21518
  res.writeHead(200, { "Content-Type": mime, "Cache-Control": "public, max-age=86400" });
21324
21519
  res.end(data);
21325
21520
  return true;
@@ -21334,15 +21529,15 @@ var init_routes = __esm({
21334
21529
  return match ? match[1] : null;
21335
21530
  }
21336
21531
  getConsensusReports(page = 1, pageSize = 5) {
21337
- const { readdirSync: readdirSync13, readFileSync: readFileSync40, existsSync: existsSync41 } = require("fs");
21338
- const reportsDir = (0, import_path45.join)(this.projectRoot, ".gossip", "consensus-reports");
21339
- if (!existsSync41(reportsDir)) return { reports: [], totalReports: 0, page, pageSize };
21532
+ const { readdirSync: readdirSync13, readFileSync: readFileSync41, existsSync: existsSync44 } = require("fs");
21533
+ const reportsDir = (0, import_path47.join)(this.projectRoot, ".gossip", "consensus-reports");
21534
+ if (!existsSync44(reportsDir)) return { reports: [], totalReports: 0, page, pageSize };
21340
21535
  try {
21341
21536
  const { statSync: statSync9 } = require("fs");
21342
21537
  const allFiles = readdirSync13(reportsDir).filter((f) => f.endsWith(".json")).sort((a, b) => {
21343
21538
  try {
21344
- const aTime = statSync9((0, import_path45.join)(reportsDir, a)).mtimeMs;
21345
- const bTime = statSync9((0, import_path45.join)(reportsDir, b)).mtimeMs;
21539
+ const aTime = statSync9((0, import_path47.join)(reportsDir, a)).mtimeMs;
21540
+ const bTime = statSync9((0, import_path47.join)(reportsDir, b)).mtimeMs;
21346
21541
  return bTime - aTime;
21347
21542
  } catch {
21348
21543
  return 0;
@@ -21353,13 +21548,13 @@ var init_routes = __esm({
21353
21548
  const clampedPage = Math.max(page, 1);
21354
21549
  const start = (clampedPage - 1) * clampedPageSize;
21355
21550
  const files = allFiles.slice(start, start + clampedPageSize);
21356
- const realReportsDir = (0, import_fs43.realpathSync)(reportsDir);
21551
+ const realReportsDir = (0, import_fs45.realpathSync)(reportsDir);
21357
21552
  const reports = files.map((f) => {
21358
21553
  try {
21359
- const filePath = (0, import_path45.join)(reportsDir, f);
21360
- const realFile = (0, import_fs43.realpathSync)(filePath);
21554
+ const filePath = (0, import_path47.join)(reportsDir, f);
21555
+ const realFile = (0, import_fs45.realpathSync)(filePath);
21361
21556
  if (!realFile.startsWith(realReportsDir + "/")) return null;
21362
- return JSON.parse(readFileSync40(realFile, "utf-8"));
21557
+ return JSON.parse(readFileSync41(realFile, "utf-8"));
21363
21558
  } catch {
21364
21559
  return null;
21365
21560
  }
@@ -21370,29 +21565,29 @@ var init_routes = __esm({
21370
21565
  }
21371
21566
  }
21372
21567
  archiveFindings() {
21373
- const { readdirSync: readdirSync13, readFileSync: readFileSync40, renameSync: renameSync2, writeFileSync: writeFileSync17, mkdirSync: mkdirSync21, existsSync: existsSync41 } = require("fs");
21374
- const reportsDir = (0, import_path45.join)(this.projectRoot, ".gossip", "consensus-reports");
21375
- const archiveDir = (0, import_path45.join)(this.projectRoot, ".gossip", "consensus-reports-archive");
21568
+ const { readdirSync: readdirSync13, readFileSync: readFileSync41, renameSync: renameSync2, writeFileSync: writeFileSync18, mkdirSync: mkdirSync22, existsSync: existsSync44 } = require("fs");
21569
+ const reportsDir = (0, import_path47.join)(this.projectRoot, ".gossip", "consensus-reports");
21570
+ const archiveDir = (0, import_path47.join)(this.projectRoot, ".gossip", "consensus-reports-archive");
21376
21571
  let archived = 0;
21377
- if (existsSync41(reportsDir)) {
21572
+ if (existsSync44(reportsDir)) {
21378
21573
  const files = readdirSync13(reportsDir).filter((f) => f.endsWith(".json")).sort().reverse();
21379
21574
  if (files.length > 5) {
21380
- mkdirSync21(archiveDir, { recursive: true });
21575
+ mkdirSync22(archiveDir, { recursive: true });
21381
21576
  const toArchive = files.slice(5);
21382
21577
  for (const f of toArchive) {
21383
21578
  try {
21384
- renameSync2((0, import_path45.join)(reportsDir, f), (0, import_path45.join)(archiveDir, f));
21579
+ renameSync2((0, import_path47.join)(reportsDir, f), (0, import_path47.join)(archiveDir, f));
21385
21580
  archived++;
21386
21581
  } catch {
21387
21582
  }
21388
21583
  }
21389
21584
  }
21390
21585
  }
21391
- const findingsPath = (0, import_path45.join)(this.projectRoot, ".gossip", "implementation-findings.jsonl");
21586
+ const findingsPath = (0, import_path47.join)(this.projectRoot, ".gossip", "implementation-findings.jsonl");
21392
21587
  let findingsCleared = 0;
21393
- if (existsSync41(findingsPath)) {
21588
+ if (existsSync44(findingsPath)) {
21394
21589
  try {
21395
- const lines = readFileSync40(findingsPath, "utf-8").trim().split("\n").filter(Boolean);
21590
+ const lines = readFileSync41(findingsPath, "utf-8").trim().split("\n").filter(Boolean);
21396
21591
  const kept = lines.filter((line) => {
21397
21592
  try {
21398
21593
  const entry = JSON.parse(line);
@@ -21405,11 +21600,11 @@ var init_routes = __esm({
21405
21600
  return true;
21406
21601
  }
21407
21602
  });
21408
- writeFileSync17(findingsPath, kept.join("\n") + (kept.length > 0 ? "\n" : ""));
21603
+ writeFileSync18(findingsPath, kept.join("\n") + (kept.length > 0 ? "\n" : ""));
21409
21604
  } catch {
21410
21605
  }
21411
21606
  }
21412
- const remaining = existsSync41(reportsDir) ? readdirSync13(reportsDir).filter((f) => f.endsWith(".json")).length : 0;
21607
+ const remaining = existsSync44(reportsDir) ? readdirSync13(reportsDir).filter((f) => f.endsWith(".json")).length : 0;
21413
21608
  return { archived, remaining, findingsCleared };
21414
21609
  }
21415
21610
  json(res, status, data) {
@@ -21422,14 +21617,14 @@ var init_routes = __esm({
21422
21617
  });
21423
21618
 
21424
21619
  // packages/relay/src/dashboard/ws.ts
21425
- var import_ws3, import_fs44, import_fs45, import_path46, DashboardWs;
21620
+ var import_ws3, import_fs46, import_fs47, import_path48, DashboardWs;
21426
21621
  var init_ws = __esm({
21427
21622
  "packages/relay/src/dashboard/ws.ts"() {
21428
21623
  "use strict";
21429
21624
  import_ws3 = require("ws");
21430
- import_fs44 = require("fs");
21431
- import_fs45 = require("fs");
21432
- import_path46 = require("path");
21625
+ import_fs46 = require("fs");
21626
+ import_fs47 = require("fs");
21627
+ import_path48 = require("path");
21433
21628
  DashboardWs = class {
21434
21629
  clients = /* @__PURE__ */ new Set();
21435
21630
  logWatcher = null;
@@ -21454,15 +21649,15 @@ var init_ws = __esm({
21454
21649
  /** Start watching mcp.log for new lines and broadcasting them to connected clients. */
21455
21650
  startLogWatcher(projectRoot) {
21456
21651
  this.stopLogWatcher();
21457
- this.logPath = (0, import_path46.join)(projectRoot, ".gossip", "mcp.log");
21458
- if (!(0, import_fs44.existsSync)(this.logPath)) return;
21652
+ this.logPath = (0, import_path48.join)(projectRoot, ".gossip", "mcp.log");
21653
+ if (!(0, import_fs46.existsSync)(this.logPath)) return;
21459
21654
  try {
21460
- this.logOffset = (0, import_fs44.statSync)(this.logPath).size;
21655
+ this.logOffset = (0, import_fs46.statSync)(this.logPath).size;
21461
21656
  } catch {
21462
21657
  this.logOffset = 0;
21463
21658
  }
21464
21659
  try {
21465
- this.logWatcher = (0, import_fs45.watch)(this.logPath, () => {
21660
+ this.logWatcher = (0, import_fs47.watch)(this.logPath, () => {
21466
21661
  if (this.clients.size === 0) return;
21467
21662
  this.readNewLines();
21468
21663
  });
@@ -21479,7 +21674,7 @@ var init_ws = __esm({
21479
21674
  if (this.logReading) return;
21480
21675
  this.logReading = true;
21481
21676
  try {
21482
- const currentSize = (0, import_fs44.statSync)(this.logPath).size;
21677
+ const currentSize = (0, import_fs46.statSync)(this.logPath).size;
21483
21678
  if (currentSize < this.logOffset) {
21484
21679
  this.logOffset = 0;
21485
21680
  this.logCarry = "";
@@ -21492,7 +21687,7 @@ var init_ws = __esm({
21492
21687
  const readFrom = capped ? (this.logCarry = "", this.logCapped = true, currentSize - 65536) : (this.logCapped = false, this.logOffset);
21493
21688
  let stream;
21494
21689
  try {
21495
- stream = (0, import_fs44.createReadStream)(this.logPath, { start: readFrom, end: currentSize - 1 });
21690
+ stream = (0, import_fs46.createReadStream)(this.logPath, { start: readFrom, end: currentSize - 1 });
21496
21691
  } catch {
21497
21692
  this.logReading = false;
21498
21693
  return;
@@ -21586,7 +21781,7 @@ var init_server = __esm({
21586
21781
  return `ws://localhost:${this._port}`;
21587
21782
  }
21588
21783
  async start() {
21589
- return new Promise((resolve17) => {
21784
+ return new Promise((resolve18) => {
21590
21785
  this.httpServer = (0, import_http.createServer)(this.handleHttp.bind(this));
21591
21786
  if (this.config.dashboard) {
21592
21787
  this.dashboardAuth = new DashboardAuth();
@@ -21631,7 +21826,7 @@ var init_server = __esm({
21631
21826
  this.httpServer.listen(this.config.port, this.config.host ?? "127.0.0.1", () => {
21632
21827
  const addr = this.httpServer.address();
21633
21828
  this._port = addr.port;
21634
- resolve17();
21829
+ resolve18();
21635
21830
  });
21636
21831
  });
21637
21832
  }
@@ -21650,9 +21845,9 @@ var init_server = __esm({
21650
21845
  for (const client of this.wss.clients) {
21651
21846
  client.close(1001, "Server shutting down");
21652
21847
  }
21653
- return new Promise((resolve17) => {
21848
+ return new Promise((resolve18) => {
21654
21849
  this.wss.close(() => {
21655
- this.httpServer.close(() => resolve17());
21850
+ this.httpServer.close(() => resolve18());
21656
21851
  });
21657
21852
  });
21658
21853
  }
@@ -21848,18 +22043,18 @@ __export(config_exports, {
21848
22043
  function findConfigPath(projectRoot) {
21849
22044
  const root = projectRoot || process.cwd();
21850
22045
  const candidates = [
21851
- (0, import_path47.resolve)(root, ".gossip", "config.json"),
21852
- (0, import_path47.resolve)(root, "gossip.agents.json"),
21853
- (0, import_path47.resolve)(root, "gossip.agents.yaml"),
21854
- (0, import_path47.resolve)(root, "gossip.agents.yml")
22046
+ (0, import_path49.resolve)(root, ".gossip", "config.json"),
22047
+ (0, import_path49.resolve)(root, "gossip.agents.json"),
22048
+ (0, import_path49.resolve)(root, "gossip.agents.yaml"),
22049
+ (0, import_path49.resolve)(root, "gossip.agents.yml")
21855
22050
  ];
21856
22051
  for (const p of candidates) {
21857
- if ((0, import_fs46.existsSync)(p)) return p;
22052
+ if ((0, import_fs48.existsSync)(p)) return p;
21858
22053
  }
21859
22054
  return null;
21860
22055
  }
21861
22056
  function loadConfig(configPath) {
21862
- const raw = (0, import_fs46.readFileSync)(configPath, "utf-8");
22057
+ const raw = (0, import_fs48.readFileSync)(configPath, "utf-8");
21863
22058
  let parsed;
21864
22059
  try {
21865
22060
  parsed = JSON.parse(raw);
@@ -21938,19 +22133,19 @@ function configToAgentConfigs(config2) {
21938
22133
  }
21939
22134
  function loadClaudeSubagents(projectRoot, existingIds) {
21940
22135
  const root = projectRoot || process.cwd();
21941
- const agentsDir = (0, import_path47.join)(root, ".claude", "agents");
21942
- if (!(0, import_fs46.existsSync)(agentsDir)) return [];
22136
+ const agentsDir = (0, import_path49.join)(root, ".claude", "agents");
22137
+ if (!(0, import_fs48.existsSync)(agentsDir)) return [];
21943
22138
  let files;
21944
22139
  try {
21945
- files = (0, import_fs46.readdirSync)(agentsDir).filter((f) => f.endsWith(".md"));
22140
+ files = (0, import_fs48.readdirSync)(agentsDir).filter((f) => f.endsWith(".md"));
21946
22141
  } catch {
21947
22142
  return [];
21948
22143
  }
21949
22144
  const agents = [];
21950
22145
  for (const file2 of files) {
21951
- const filePath = (0, import_path47.join)(agentsDir, file2);
22146
+ const filePath = (0, import_path49.join)(agentsDir, file2);
21952
22147
  try {
21953
- const content = (0, import_fs46.readFileSync)(filePath, "utf-8");
22148
+ const content = (0, import_fs48.readFileSync)(filePath, "utf-8");
21954
22149
  const frontmatter = content.match(/^---\n([\s\S]*?)\n---/);
21955
22150
  if (!frontmatter) continue;
21956
22151
  const fm = frontmatter[1];
@@ -22005,12 +22200,12 @@ function inferSkills(description, name) {
22005
22200
  if (skills.length === 0) skills.push("general");
22006
22201
  return skills;
22007
22202
  }
22008
- var import_fs46, import_path47, VALID_PROVIDERS, CLAUDE_MODEL_MAP;
22203
+ var import_fs48, import_path49, VALID_PROVIDERS, CLAUDE_MODEL_MAP;
22009
22204
  var init_config = __esm({
22010
22205
  "apps/cli/src/config.ts"() {
22011
22206
  "use strict";
22012
- import_fs46 = require("fs");
22013
- import_path47 = require("path");
22207
+ import_fs48 = require("fs");
22208
+ import_path49 = require("path");
22014
22209
  VALID_PROVIDERS = ["anthropic", "openai", "openclaw", "google", "local", "native"];
22015
22210
  CLAUDE_MODEL_MAP = {
22016
22211
  opus: { provider: "anthropic", model: "claude-opus-4-6" },
@@ -22025,14 +22220,14 @@ var keychain_exports = {};
22025
22220
  __export(keychain_exports, {
22026
22221
  Keychain: () => Keychain
22027
22222
  });
22028
- var import_child_process5, import_os2, import_fs47, import_path48, import_crypto15, SERVICE_NAME, VALID_PROVIDERS2, ENCRYPTED_FILE, ALGO, Keychain;
22223
+ var import_child_process5, import_os2, import_fs49, import_path50, import_crypto15, SERVICE_NAME, VALID_PROVIDERS2, ENCRYPTED_FILE, ALGO, Keychain;
22029
22224
  var init_keychain = __esm({
22030
22225
  "apps/cli/src/keychain.ts"() {
22031
22226
  "use strict";
22032
22227
  import_child_process5 = require("child_process");
22033
22228
  import_os2 = require("os");
22034
- import_fs47 = require("fs");
22035
- import_path48 = require("path");
22229
+ import_fs49 = require("fs");
22230
+ import_path50 = require("path");
22036
22231
  import_crypto15 = require("crypto");
22037
22232
  SERVICE_NAME = "gossip-mesh";
22038
22233
  VALID_PROVIDERS2 = /^[a-zA-Z0-9_-]{1,32}$/;
@@ -22074,10 +22269,10 @@ var init_keychain = __esm({
22074
22269
  return (0, import_crypto15.pbkdf2Sync)(seed, salt, 6e5, 32, "sha256");
22075
22270
  }
22076
22271
  loadEncryptedFile() {
22077
- const filePath = (0, import_path48.join)(process.cwd(), ENCRYPTED_FILE);
22078
- if (!(0, import_fs47.existsSync)(filePath)) return;
22272
+ const filePath = (0, import_path50.join)(process.cwd(), ENCRYPTED_FILE);
22273
+ if (!(0, import_fs49.existsSync)(filePath)) return;
22079
22274
  try {
22080
- const raw = (0, import_fs47.readFileSync)(filePath);
22275
+ const raw = (0, import_fs49.readFileSync)(filePath);
22081
22276
  if (raw.length < 61) return;
22082
22277
  const salt = raw.subarray(0, 32);
22083
22278
  const iv = raw.subarray(32, 44);
@@ -22095,9 +22290,9 @@ var init_keychain = __esm({
22095
22290
  }
22096
22291
  }
22097
22292
  saveEncryptedFile() {
22098
- const filePath = (0, import_path48.join)(process.cwd(), ENCRYPTED_FILE);
22099
- const dir = (0, import_path48.join)(process.cwd(), ".gossip");
22100
- if (!(0, import_fs47.existsSync)(dir)) (0, import_fs47.mkdirSync)(dir, { recursive: true });
22293
+ const filePath = (0, import_path50.join)(process.cwd(), ENCRYPTED_FILE);
22294
+ const dir = (0, import_path50.join)(process.cwd(), ".gossip");
22295
+ if (!(0, import_fs49.existsSync)(dir)) (0, import_fs49.mkdirSync)(dir, { recursive: true });
22101
22296
  const data = JSON.stringify(Object.fromEntries(this.inMemoryStore));
22102
22297
  const salt = (0, import_crypto15.randomBytes)(32);
22103
22298
  const iv = (0, import_crypto15.randomBytes)(12);
@@ -22105,7 +22300,7 @@ var init_keychain = __esm({
22105
22300
  const cipher = (0, import_crypto15.createCipheriv)(ALGO, key, iv);
22106
22301
  const encrypted = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
22107
22302
  const tag = cipher.getAuthTag();
22108
- (0, import_fs47.writeFileSync)(filePath, Buffer.concat([salt, iv, tag, encrypted]), { mode: 384 });
22303
+ (0, import_fs49.writeFileSync)(filePath, Buffer.concat([salt, iv, tag, encrypted]), { mode: 384 });
22109
22304
  }
22110
22305
  isKeychainAvailable() {
22111
22306
  if ((0, import_os2.platform)() === "darwin") {
@@ -22205,17 +22400,17 @@ __export(identity_exports, {
22205
22400
  normalizeGitUrl: () => normalizeGitUrl
22206
22401
  });
22207
22402
  function getOrCreateSalt(projectRoot) {
22208
- const saltPath = (0, import_path49.join)(projectRoot, ".gossip", "local-salt");
22403
+ const saltPath = (0, import_path51.join)(projectRoot, ".gossip", "local-salt");
22209
22404
  try {
22210
- return (0, import_fs48.readFileSync)(saltPath, "utf-8").trim();
22405
+ return (0, import_fs50.readFileSync)(saltPath, "utf-8").trim();
22211
22406
  } catch {
22212
22407
  const salt = (0, import_crypto16.randomBytes)(16).toString("hex");
22213
- (0, import_fs48.mkdirSync)((0, import_path49.join)(projectRoot, ".gossip"), { recursive: true });
22408
+ (0, import_fs50.mkdirSync)((0, import_path51.join)(projectRoot, ".gossip"), { recursive: true });
22214
22409
  try {
22215
- (0, import_fs48.writeFileSync)(saltPath, salt, { flag: "wx" });
22410
+ (0, import_fs50.writeFileSync)(saltPath, salt, { flag: "wx" });
22216
22411
  return salt;
22217
22412
  } catch {
22218
- return (0, import_fs48.readFileSync)(saltPath, "utf-8").trim();
22413
+ return (0, import_fs50.readFileSync)(saltPath, "utf-8").trim();
22219
22414
  }
22220
22415
  }
22221
22416
  }
@@ -22265,12 +22460,12 @@ function getProjectId(projectRoot) {
22265
22460
  }
22266
22461
  return (0, import_crypto16.createHash)("sha256").update(projectRoot).digest("hex").slice(0, 16);
22267
22462
  }
22268
- var import_fs48, import_path49, import_crypto16, import_child_process6;
22463
+ var import_fs50, import_path51, import_crypto16, import_child_process6;
22269
22464
  var init_identity = __esm({
22270
22465
  "apps/cli/src/identity.ts"() {
22271
22466
  "use strict";
22272
- import_fs48 = require("fs");
22273
- import_path49 = require("path");
22467
+ import_fs50 = require("fs");
22468
+ import_path51 = require("path");
22274
22469
  import_crypto16 = require("crypto");
22275
22470
  import_child_process6 = require("child_process");
22276
22471
  }
@@ -22282,14 +22477,8 @@ __export(gossip_update_exports, {
22282
22477
  handleGossipUpdate: () => handleGossipUpdate
22283
22478
  });
22284
22479
  function getCurrentVersion() {
22285
- try {
22286
- const pkgPath = (0, import_path50.resolve)(__dirname, "..", "..", "..", "..", "package.json");
22287
- if ((0, import_fs49.existsSync)(pkgPath)) {
22288
- return JSON.parse((0, import_fs49.readFileSync)(pkgPath, "utf-8")).version ?? "0.0.0";
22289
- }
22290
- } catch {
22291
- }
22292
- return "0.0.0";
22480
+ const v = getGossipcatVersion();
22481
+ return v === "unknown" ? "0.0.0" : v;
22293
22482
  }
22294
22483
  async function getLatestVersion() {
22295
22484
  const res = await fetch("https://registry.npmjs.org/gossipcat/latest");
@@ -22298,9 +22487,9 @@ async function getLatestVersion() {
22298
22487
  return data.version;
22299
22488
  }
22300
22489
  function detectInstallMethod() {
22301
- const packageRoot = (0, import_path50.resolve)(__dirname, "..", "..", "..", "..");
22490
+ const packageRoot = (0, import_path52.resolve)(__dirname, "..", "..", "..", "..");
22302
22491
  if (process.env.npm_config_global === "true") return "global";
22303
- if ((0, import_fs49.existsSync)((0, import_path50.join)(packageRoot, ".git"))) return "git-clone";
22492
+ if ((0, import_fs51.existsSync)((0, import_path52.join)(packageRoot, ".git"))) return "git-clone";
22304
22493
  return "local";
22305
22494
  }
22306
22495
  function updateCommand(method, version2) {
@@ -22351,7 +22540,7 @@ Check your internet connection or visit https://www.npmjs.com/package/gossipcat
22351
22540
  try {
22352
22541
  (0, import_child_process7.execSync)(command, {
22353
22542
  stdio: "inherit",
22354
- cwd: method === "git-clone" ? (0, import_path50.resolve)(__dirname, "..", "..", "..", "..") : process.cwd()
22543
+ cwd: method === "git-clone" ? (0, import_path52.resolve)(__dirname, "..", "..", "..", "..") : process.cwd()
22355
22544
  });
22356
22545
  } catch (err) {
22357
22546
  return {
@@ -22373,13 +22562,14 @@ Run /mcp reconnect in Claude Code to load the new version.`
22373
22562
  }]
22374
22563
  };
22375
22564
  }
22376
- var import_child_process7, import_fs49, import_path50;
22565
+ var import_child_process7, import_fs51, import_path52;
22377
22566
  var init_gossip_update = __esm({
22378
22567
  "apps/cli/src/handlers/gossip-update.ts"() {
22379
22568
  "use strict";
22380
22569
  import_child_process7 = require("child_process");
22381
- import_fs49 = require("fs");
22382
- import_path50 = require("path");
22570
+ import_fs51 = require("fs");
22571
+ import_path52 = require("path");
22572
+ init_version();
22383
22573
  }
22384
22574
  });
22385
22575
 
@@ -22542,8 +22732,8 @@ var init_verify_memory = __esm({
22542
22732
  });
22543
22733
 
22544
22734
  // apps/cli/src/mcp-server-sdk.ts
22545
- var import_fs50 = require("fs");
22546
- var import_path51 = require("path");
22735
+ var import_fs52 = require("fs");
22736
+ var import_path53 = require("path");
22547
22737
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
22548
22738
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
22549
22739
  var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
@@ -36320,6 +36510,7 @@ config(en_default());
36320
36510
  var import_crypto17 = require("crypto");
36321
36511
  var import_http2 = require("http");
36322
36512
  init_mcp_context();
36513
+ init_version();
36323
36514
 
36324
36515
  // apps/cli/src/handlers/native-tasks.ts
36325
36516
  var import_crypto10 = require("crypto");
@@ -36724,8 +36915,8 @@ ${utilityBlocks.join("\n\n")}`;
36724
36915
 
36725
36916
  // apps/cli/src/handlers/dispatch.ts
36726
36917
  var import_crypto11 = require("crypto");
36727
- var import_fs31 = require("fs");
36728
- var import_path33 = require("path");
36918
+ var import_fs32 = require("fs");
36919
+ var import_path34 = require("path");
36729
36920
  init_src4();
36730
36921
  init_src3();
36731
36922
  init_mcp_context();
@@ -36842,7 +37033,7 @@ var AGENT_PROVIDER_MAP = {
36842
37033
  };
36843
37034
  function readQuotaState() {
36844
37035
  try {
36845
- const raw = (0, import_fs31.readFileSync)((0, import_path33.join)(process.cwd(), ".gossip", "quota-state.json"), "utf8");
37036
+ const raw = (0, import_fs32.readFileSync)((0, import_path34.join)(process.cwd(), ".gossip", "quota-state.json"), "utf8");
36846
37037
  return JSON.parse(raw);
36847
37038
  } catch {
36848
37039
  return {};
@@ -37188,8 +37379,8 @@ async function handleDispatchConsensus(taskDefs, _utility_task_id) {
37188
37379
  });
37189
37380
  const lenses = await Promise.race([
37190
37381
  lensPromise,
37191
- new Promise((resolve17) => {
37192
- timerId = setTimeout(() => resolve17(null), LENS_TIMEOUT_MS);
37382
+ new Promise((resolve18) => {
37383
+ timerId = setTimeout(() => resolve18(null), LENS_TIMEOUT_MS);
37193
37384
  })
37194
37385
  ]);
37195
37386
  if (timerId) clearTimeout(timerId);
@@ -37405,7 +37596,7 @@ Relay may be down. Check gossip_status() for connection state.` }] };
37405
37596
  process.stderr.write(`[gossipcat] \u23F3 Consensus: ${doneCount}/${pendingNativeIds.length} agents complete (${agentStatus})
37406
37597
  `);
37407
37598
  }
37408
- await new Promise((resolve17) => setTimeout(resolve17, POLL_INTERVAL));
37599
+ await new Promise((resolve18) => setTimeout(resolve18, POLL_INTERVAL));
37409
37600
  }
37410
37601
  const arrived = pendingNativeIds.filter((id) => ctx.nativeResultMap.has(id)).length;
37411
37602
  const timedOutCount = pendingNativeIds.filter((id) => {
@@ -37902,12 +38093,83 @@ ${gaps.map((g) => ` - ${g.agentId} needs "${g.category}" (score: ${g.score.toFi
37902
38093
 
37903
38094
  // apps/cli/src/mcp-server-sdk.ts
37904
38095
  init_relay_cross_review();
37905
- var gossipDir = (0, import_path51.join)(process.cwd(), ".gossip");
38096
+
38097
+ // apps/cli/src/stickyPort.ts
38098
+ var import_fs34 = require("fs");
38099
+ var import_path36 = require("path");
38100
+ var import_net = require("net");
38101
+ var RELAY_STICKY_FILE = (0, import_path36.join)(".gossip", "relay.port");
38102
+ var HTTP_MCP_STICKY_FILE = (0, import_path36.join)(".gossip", "http-mcp.port");
38103
+ function stickyPath(filename) {
38104
+ return (0, import_path36.join)(process.cwd(), filename);
38105
+ }
38106
+ function readStickyPort(filename) {
38107
+ const p = stickyPath(filename);
38108
+ if (!(0, import_fs34.existsSync)(p)) return null;
38109
+ try {
38110
+ const raw = (0, import_fs34.readFileSync)(p, "utf-8").trim();
38111
+ const n = parseInt(raw, 10);
38112
+ if (!Number.isFinite(n) || n < 1 || n > 65535) return null;
38113
+ return n;
38114
+ } catch {
38115
+ return null;
38116
+ }
38117
+ }
38118
+ function writeStickyPort(filename, port) {
38119
+ if (!Number.isFinite(port) || port < 1 || port > 65535) return;
38120
+ const p = stickyPath(filename);
38121
+ try {
38122
+ (0, import_fs34.mkdirSync)((0, import_path36.dirname)(p), { recursive: true });
38123
+ (0, import_fs34.writeFileSync)(p, String(port), "utf-8");
38124
+ } catch {
38125
+ }
38126
+ }
38127
+ function probePort(port, host = "127.0.0.1") {
38128
+ return new Promise((resolve18) => {
38129
+ const srv = (0, import_net.createServer)();
38130
+ let settled = false;
38131
+ const done = (ok) => {
38132
+ if (settled) return;
38133
+ settled = true;
38134
+ try {
38135
+ srv.close();
38136
+ } catch {
38137
+ }
38138
+ resolve18(ok);
38139
+ };
38140
+ srv.once("error", () => done(false));
38141
+ try {
38142
+ srv.listen(port, host, () => {
38143
+ srv.close(() => done(true));
38144
+ });
38145
+ } catch {
38146
+ done(false);
38147
+ }
38148
+ });
38149
+ }
38150
+ async function pickStickyPort(envVar, stickyFile, probeHost = "127.0.0.1") {
38151
+ const envRaw = process.env[envVar];
38152
+ if (envRaw !== void 0 && envRaw !== "") {
38153
+ const envPort = parseInt(envRaw, 10);
38154
+ if (Number.isFinite(envPort) && envPort >= 0 && envPort <= 65535) {
38155
+ return { port: envPort, source: "env" };
38156
+ }
38157
+ }
38158
+ const sticky = readStickyPort(stickyFile);
38159
+ if (sticky !== null) {
38160
+ const free = await probePort(sticky, probeHost);
38161
+ if (free) return { port: sticky, source: "sticky" };
38162
+ }
38163
+ return { port: 0, source: "auto" };
38164
+ }
38165
+
38166
+ // apps/cli/src/mcp-server-sdk.ts
38167
+ var gossipDir = (0, import_path53.join)(process.cwd(), ".gossip");
37906
38168
  try {
37907
- (0, import_fs50.mkdirSync)(gossipDir, { recursive: true });
38169
+ (0, import_fs52.mkdirSync)(gossipDir, { recursive: true });
37908
38170
  } catch {
37909
38171
  }
37910
- var logStream = (0, import_fs50.createWriteStream)((0, import_path51.join)(gossipDir, "mcp.log"), { flags: "a" });
38172
+ var logStream = (0, import_fs52.createWriteStream)((0, import_path53.join)(gossipDir, "mcp.log"), { flags: "a" });
37911
38173
  process.stderr.write = ((chunk, ...args) => {
37912
38174
  return logStream.write(chunk, ...args);
37913
38175
  });
@@ -38071,17 +38333,18 @@ var booted = false;
38071
38333
  var bootPromise = null;
38072
38334
  var planExecutionDepth = 0;
38073
38335
  var _pendingSessionData = /* @__PURE__ */ new Map();
38336
+ var _pendingSkillData = /* @__PURE__ */ new Map();
38074
38337
  var _pendingVerifyData = /* @__PURE__ */ new Map();
38075
38338
  var _modules = null;
38076
38339
  function lookupFindingSeverity(findingId, projectRoot) {
38077
- const { existsSync: existsSync41, readdirSync: readdirSync13, readFileSync: readFileSync40 } = require("fs");
38078
- const { join: join48 } = require("path");
38079
- const reportsDir = join48(projectRoot, ".gossip", "consensus-reports");
38080
- if (!existsSync41(reportsDir)) return null;
38340
+ const { existsSync: existsSync44, readdirSync: readdirSync13, readFileSync: readFileSync41 } = require("fs");
38341
+ const { join: join49 } = require("path");
38342
+ const reportsDir = join49(projectRoot, ".gossip", "consensus-reports");
38343
+ if (!existsSync44(reportsDir)) return null;
38081
38344
  try {
38082
38345
  const files = readdirSync13(reportsDir).filter((f) => f.endsWith(".json"));
38083
38346
  for (const file2 of files) {
38084
- const report = JSON.parse(readFileSync40(join48(reportsDir, file2), "utf-8"));
38347
+ const report = JSON.parse(readFileSync41(join49(reportsDir, file2), "utf-8"));
38085
38348
  for (const bucket of ["confirmed", "disputed", "unverified", "unique"]) {
38086
38349
  for (const finding of report[bucket] || []) {
38087
38350
  if (finding.id === findingId && finding.severity) {
@@ -38144,12 +38407,21 @@ async function refreshBootstrap() {
38144
38407
  async function doBoot() {
38145
38408
  const m = await getModules();
38146
38409
  const configPath = m.findConfigPath();
38147
- if (!configPath) throw new Error("No gossip.agents.json found. Run gossipcat setup first.");
38148
- const config2 = m.loadConfig(configPath);
38410
+ let config2;
38411
+ if (configPath) {
38412
+ config2 = m.loadConfig(configPath);
38413
+ } else {
38414
+ process.stderr.write("[gossipcat] \u26A0\uFE0F No gossip.agents.json found \u2014 booting in degraded mode (dashboard + relay only). Run gossip_setup inside Claude Code to create your agent team.\n");
38415
+ config2 = {
38416
+ main_agent: { provider: "none", model: "none" },
38417
+ utility_model: { provider: "none", model: "none" },
38418
+ agents: {}
38419
+ };
38420
+ }
38149
38421
  const agentConfigs = m.configToAgentConfigs(config2);
38150
38422
  ctx.keychain = new m.Keychain();
38151
38423
  const { existsSync: pidExists, readFileSync: readPid, writeFileSync: writePid, unlinkSync: delPid } = require("fs");
38152
- const pidFile = (0, import_path51.join)(process.cwd(), ".gossip", "relay.pid");
38424
+ const pidFile = (0, import_path53.join)(process.cwd(), ".gossip", "relay.pid");
38153
38425
  if (pidExists(pidFile)) {
38154
38426
  const oldPid = parseInt(readPid(pidFile, "utf-8").trim(), 10);
38155
38427
  if (!isNaN(oldPid) && oldPid !== process.pid) {
@@ -38162,8 +38434,11 @@ async function doBoot() {
38162
38434
  }
38163
38435
  }
38164
38436
  const relayApiKey = (0, import_crypto17.randomBytes)(32).toString("hex");
38437
+ const relayPick = await pickStickyPort("GOSSIPCAT_PORT", RELAY_STICKY_FILE);
38438
+ const relayPort = relayPick.port;
38439
+ ctx.relayPortSource = relayPick.source;
38165
38440
  ctx.relay = new m.RelayServer({
38166
- port: 24420,
38441
+ port: relayPort,
38167
38442
  apiKey: relayApiKey,
38168
38443
  dashboard: {
38169
38444
  projectRoot: process.cwd(),
@@ -38171,7 +38446,10 @@ async function doBoot() {
38171
38446
  }
38172
38447
  });
38173
38448
  await ctx.relay.start();
38174
- startHttpMcpTransport();
38449
+ if (typeof ctx.relay.port === "number" && ctx.relay.port > 0) {
38450
+ writeStickyPort(RELAY_STICKY_FILE, ctx.relay.port);
38451
+ }
38452
+ await startHttpMcpTransport();
38175
38453
  try {
38176
38454
  writePid(pidFile, String(process.pid));
38177
38455
  } catch {
@@ -38237,10 +38515,10 @@ async function doBoot() {
38237
38515
  }
38238
38516
  const key = await ctx.keychain.getKey(ac.provider);
38239
38517
  const llm = m.createProvider(ac.provider, ac.model, key ?? void 0, void 0, ac.base_url);
38240
- const { existsSync: existsSync41, readFileSync: readFileSync40 } = require("fs");
38241
- const { join: join48 } = require("path");
38242
- const instructionsPath = join48(process.cwd(), ".gossip", "agents", ac.id, "instructions.md");
38243
- const baseInstructions = existsSync41(instructionsPath) ? readFileSync40(instructionsPath, "utf-8") : "";
38518
+ const { existsSync: existsSync44, readFileSync: readFileSync41 } = require("fs");
38519
+ const { join: join49 } = require("path");
38520
+ const instructionsPath = join49(process.cwd(), ".gossip", "agents", ac.id, "instructions.md");
38521
+ const baseInstructions = existsSync44(instructionsPath) ? readFileSync41(instructionsPath, "utf-8") : "";
38244
38522
  const identity = identityRegistry.get(ac.id);
38245
38523
  const identityBlock = identity ? m.formatIdentityBlock(identity) + "\n" : "";
38246
38524
  const instructions = (identityBlock + baseInstructions).trim() || void 0;
@@ -38298,8 +38576,13 @@ async function doBoot() {
38298
38576
  if (!mainKey) {
38299
38577
  mainProvider = "none";
38300
38578
  config2.main_agent.provider = "none";
38301
- process.stderr.write(`[gossipcat] \u274C No API keys available \u2014 orchestrator LLM disabled, features degrade to profile-based
38579
+ if (env.host === "claude-code") {
38580
+ process.stderr.write(`[gossipcat] \u2705 Native Claude Code orchestration enabled (no API LLM needed \u2014 host classifies via natural language)
38581
+ `);
38582
+ } else {
38583
+ process.stderr.write(`[gossipcat] \u274C No API keys available \u2014 orchestrator LLM disabled, features degrade to profile-based
38302
38584
  `);
38585
+ }
38303
38586
  }
38304
38587
  }
38305
38588
  ctx.mainProvider = mainProvider;
@@ -38572,7 +38855,7 @@ ctx.getModules = getModules;
38572
38855
  var server = new import_mcp.McpServer(
38573
38856
  {
38574
38857
  name: "gossipcat",
38575
- version: "0.1.0"
38858
+ version: getGossipcatVersion()
38576
38859
  },
38577
38860
  {
38578
38861
  instructions: "gossipcat \u2014 multi-agent orchestration. ALWAYS call gossip_status() first when starting work in this project. The gossip_status response loads the orchestrator role, dispatch rules, consensus workflow, native agent relay rule, sandbox enforcement, and other operating rules from .gossip/rules.md. These rules are not in this instruction text \u2014 they live in the gossip_status output to keep the instruction surface small and to allow per-project customization."
@@ -38796,18 +39079,21 @@ server.tool(
38796
39079
  "Status:",
38797
39080
  ` Host: ${env.host}${env.supportsNativeAgents ? " (native agents supported)" : ""}`,
38798
39081
  ` Native agent dir: ${env.nativeAgentDir || "n/a"}`,
38799
- ` Relay: ${ctx.relay ? `running :${ctx.relay.port}` : "not started"}`,
39082
+ ` Relay: ${ctx.relay ? `running :${ctx.relay.port}${ctx.relayPortSource === "sticky" ? " (sticky)" : ""}` : "not started"}`,
38800
39083
  ` Tool Server: ${ctx.toolServer ? "running" : "not started"}`,
38801
39084
  ` Workers: ${ctx.workers.size} (${Array.from(ctx.workers.keys()).join(", ") || "none"})`,
38802
39085
  ` Claude subagents found: ${claudeSubagentsList.length}`
38803
39086
  ];
38804
39087
  if (ctx.relay?.dashboardUrl) {
38805
- lines.push(` Dashboard: ${ctx.relay.dashboardUrl} (key: ${ctx.relay.dashboardKey})`);
39088
+ lines.push(` Dashboard: ${ctx.relay.dashboardUrl}${ctx.relayPortSource === "sticky" ? " (sticky)" : ""} (key: ${ctx.relay.dashboardKey})`);
39089
+ }
39090
+ if (ctx.httpMcpPort) {
39091
+ lines.push(` HTTP MCP: :${ctx.httpMcpPort}/mcp${ctx.httpMcpPortSource === "sticky" ? " (sticky)" : ""}`);
38806
39092
  }
38807
39093
  try {
38808
- const { readFileSync: readFileSync40 } = await import("fs");
38809
- const quotaPath = (0, import_path51.join)(process.cwd(), ".gossip", "quota-state.json");
38810
- const quotaRaw = readFileSync40(quotaPath, "utf8");
39094
+ const { readFileSync: readFileSync41 } = await import("fs");
39095
+ const quotaPath = (0, import_path53.join)(process.cwd(), ".gossip", "quota-state.json");
39096
+ const quotaRaw = readFileSync41(quotaPath, "utf8");
38811
39097
  const quotaState = JSON.parse(quotaRaw);
38812
39098
  for (const [provider, state] of Object.entries(quotaState)) {
38813
39099
  const now = Date.now();
@@ -38821,16 +39107,16 @@ server.tool(
38821
39107
  } catch {
38822
39108
  }
38823
39109
  try {
38824
- const { readFileSync: readFileSync40, readdirSync: readdirSync13, statSync: statSync9 } = await import("fs");
38825
- const reportsDir = (0, import_path51.join)(process.cwd(), ".gossip", "consensus-reports");
38826
- const perfPath = (0, import_path51.join)(process.cwd(), ".gossip", "agent-performance.jsonl");
39110
+ const { readFileSync: readFileSync41, readdirSync: readdirSync13, statSync: statSync9 } = await import("fs");
39111
+ const reportsDir = (0, import_path53.join)(process.cwd(), ".gossip", "consensus-reports");
39112
+ const perfPath = (0, import_path53.join)(process.cwd(), ".gossip", "agent-performance.jsonl");
38827
39113
  const WINDOW_MS = 24 * 60 * 60 * 1e3;
38828
39114
  const now = Date.now();
38829
39115
  const recentReports = [];
38830
39116
  try {
38831
39117
  for (const fname of readdirSync13(reportsDir)) {
38832
39118
  if (!fname.endsWith(".json")) continue;
38833
- const fpath = (0, import_path51.join)(reportsDir, fname);
39119
+ const fpath = (0, import_path53.join)(reportsDir, fname);
38834
39120
  const st = statSync9(fpath);
38835
39121
  if (now - st.mtimeMs > WINDOW_MS) continue;
38836
39122
  recentReports.push({ id: fname.replace(/\.json$/, ""), mtimeMs: st.mtimeMs });
@@ -38840,7 +39126,7 @@ server.tool(
38840
39126
  if (recentReports.length > 0) {
38841
39127
  const covered = /* @__PURE__ */ new Set();
38842
39128
  try {
38843
- const perfRaw = readFileSync40(perfPath, "utf8");
39129
+ const perfRaw = readFileSync41(perfPath, "utf8");
38844
39130
  for (const line of perfRaw.split("\n")) {
38845
39131
  if (!line) continue;
38846
39132
  try {
@@ -38972,7 +39258,73 @@ ${toolsMatch[1].trim()}`;
38972
39258
  }
38973
39259
  } catch {
38974
39260
  }
38975
- return { content: [{ type: "text", text: lines.join("\n") + "\n\n" + agentSections.join("\n") + sessionContextSection }] };
39261
+ let handbookSection = "";
39262
+ try {
39263
+ const { readFileSync: rfHB, statSync: stHB } = require("fs");
39264
+ const { join: jHB } = require("path");
39265
+ const handbookPath = jHB(process.cwd(), "docs", "HANDBOOK.md");
39266
+ const stat3 = stHB(handbookPath);
39267
+ const HANDBOOK_CAP_BYTES = 24 * 1024;
39268
+ let body = rfHB(handbookPath, "utf-8");
39269
+ const truncated = body.length > HANDBOOK_CAP_BYTES;
39270
+ if (truncated) {
39271
+ body = body.slice(0, HANDBOOK_CAP_BYTES);
39272
+ }
39273
+ handbookSection = "\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n## Project Handbook (auto-loaded from docs/HANDBOOK.md)\n\n" + body.trim() + (truncated ? `
39274
+
39275
+ [handbook truncated at ${HANDBOOK_CAP_BYTES / 1024}KB \u2014 full file at docs/HANDBOOK.md, ${stat3.size} bytes total]` : "");
39276
+ } catch {
39277
+ }
39278
+ return { content: [{ type: "text", text: lines.join("\n") + "\n\n" + agentSections.join("\n") + sessionContextSection + handbookSection }] };
39279
+ }
39280
+ );
39281
+ server.tool(
39282
+ "gossip_guide",
39283
+ "Show the gossipcat handbook for humans \u2014 architectural invariants, operator playbook, caveats, hallucination patterns, glossary. Call this when you want to READ the docs, not when you need LLM context. The LLM-facing auto-load lives in gossip_status().",
39284
+ {},
39285
+ async () => {
39286
+ try {
39287
+ const { readFileSync: rfG, existsSync: exG } = require("fs");
39288
+ const { join: jG, dirname: dG } = require("path");
39289
+ const projectHandbook = jG(process.cwd(), "docs", "HANDBOOK.md");
39290
+ if (exG(projectHandbook)) {
39291
+ const body = rfG(projectHandbook, "utf-8");
39292
+ return {
39293
+ content: [{
39294
+ type: "text",
39295
+ text: `# Gossipcat Handbook (from ${projectHandbook})
39296
+
39297
+ ${body}`
39298
+ }]
39299
+ };
39300
+ }
39301
+ const bundleRoot = dG(require.resolve("../../dist-mcp/mcp-server.js").replace(/\/dist-mcp\/mcp-server\.js$/, "/dist-mcp/mcp-server.js"));
39302
+ const defaultHandbook = jG(bundleRoot, "..", "docs", "HANDBOOK.md");
39303
+ if (exG(defaultHandbook)) {
39304
+ const body = rfG(defaultHandbook, "utf-8");
39305
+ return {
39306
+ content: [{
39307
+ type: "text",
39308
+ text: `# Gossipcat Handbook (bundled default \u2014 your project has no docs/HANDBOOK.md yet)
39309
+
39310
+ ${body}`
39311
+ }]
39312
+ };
39313
+ }
39314
+ return {
39315
+ content: [{
39316
+ type: "text",
39317
+ text: "No handbook found. Expected at docs/HANDBOOK.md in the current project or bundled with gossipcat. If you are inside a gossipcat project, run `gossip_setup` first."
39318
+ }]
39319
+ };
39320
+ } catch (err) {
39321
+ return {
39322
+ content: [{
39323
+ type: "text",
39324
+ text: `Error reading handbook: ${err.message}`
39325
+ }]
39326
+ };
39327
+ }
38976
39328
  }
38977
39329
  );
38978
39330
  server.tool(
@@ -39079,8 +39431,8 @@ server.tool(
39079
39431
  }
39080
39432
  return { content: [{ type: "text", text: results.join("\n") }] };
39081
39433
  }
39082
- const { writeFileSync: writeFileSync17, mkdirSync: mkdirSync21, existsSync: existsSync41 } = require("fs");
39083
- const { join: join48 } = require("path");
39434
+ const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22, existsSync: existsSync44 } = require("fs");
39435
+ const { join: join49 } = require("path");
39084
39436
  const root = process.cwd();
39085
39437
  const CLAUDE_MODEL_MAP2 = {
39086
39438
  opus: { provider: "anthropic", model: "claude-opus-4-6" },
@@ -39094,8 +39446,8 @@ server.tool(
39094
39446
  let existingAgents = {};
39095
39447
  if (mode === "merge") {
39096
39448
  try {
39097
- const { readFileSync: readFileSync40 } = require("fs");
39098
- const existing = JSON.parse(readFileSync40(join48(root, ".gossip", "config.json"), "utf-8"));
39449
+ const { readFileSync: readFileSync41 } = require("fs");
39450
+ const existing = JSON.parse(readFileSync41(join49(root, ".gossip", "config.json"), "utf-8"));
39099
39451
  existingAgents = existing.agents || {};
39100
39452
  } catch {
39101
39453
  }
@@ -39127,9 +39479,9 @@ server.tool(
39127
39479
  "",
39128
39480
  body
39129
39481
  ].join("\n");
39130
- const agentsDir = join48(root, ".claude", "agents");
39131
- mkdirSync21(agentsDir, { recursive: true });
39132
- writeFileSync17(join48(agentsDir, `${agent.id}.md`), md, "utf-8");
39482
+ const agentsDir = join49(root, ".claude", "agents");
39483
+ mkdirSync22(agentsDir, { recursive: true });
39484
+ writeFileSync18(join49(agentsDir, `${agent.id}.md`), md, "utf-8");
39133
39485
  nativeCreated.push(agent.id);
39134
39486
  configAgents[agent.id] = {
39135
39487
  provider: mapped.provider,
@@ -39147,8 +39499,8 @@ server.tool(
39147
39499
  errors.push(`${agent.id}: custom agent requires "custom_model" field`);
39148
39500
  continue;
39149
39501
  }
39150
- const nativeFile = join48(root, ".claude", "agents", `${agent.id}.md`);
39151
- const wasNative = existingAgents[agent.id]?.native || existsSync41(nativeFile);
39502
+ const nativeFile = join49(root, ".claude", "agents", `${agent.id}.md`);
39503
+ const wasNative = existingAgents[agent.id]?.native || existsSync44(nativeFile);
39152
39504
  if (wasNative) {
39153
39505
  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.`);
39154
39506
  continue;
@@ -39162,9 +39514,9 @@ server.tool(
39162
39514
  };
39163
39515
  customCreated.push(agent.id);
39164
39516
  if (agent.instructions) {
39165
- const instrDir = join48(root, ".gossip", "agents", agent.id);
39166
- mkdirSync21(instrDir, { recursive: true });
39167
- writeFileSync17(join48(instrDir, "instructions.md"), agent.instructions, "utf-8");
39517
+ const instrDir = join49(root, ".gossip", "agents", agent.id);
39518
+ mkdirSync22(instrDir, { recursive: true });
39519
+ writeFileSync18(join49(instrDir, "instructions.md"), agent.instructions, "utf-8");
39168
39520
  }
39169
39521
  }
39170
39522
  }
@@ -39178,13 +39530,13 @@ server.tool(
39178
39530
  } catch (err) {
39179
39531
  return { content: [{ type: "text", text: `Invalid config: ${err.message}` }] };
39180
39532
  }
39181
- mkdirSync21(join48(root, ".gossip"), { recursive: true });
39182
- writeFileSync17(join48(root, ".gossip", "config.json"), JSON.stringify(config2, null, 2));
39533
+ mkdirSync22(join49(root, ".gossip"), { recursive: true });
39534
+ writeFileSync18(join49(root, ".gossip", "config.json"), JSON.stringify(config2, null, 2));
39183
39535
  const agentList = Object.entries(config2.agents).map(([id, a]) => `- ${id}: ${a.provider}/${a.model} (${a.preset || "custom"})${a.native ? " \u2014 native" : ""}`).join("\n");
39184
- const rulesDir = join48(root, env.rulesDir);
39185
- const rulesFile = join48(root, env.rulesFile);
39186
- mkdirSync21(rulesDir, { recursive: true });
39187
- writeFileSync17(rulesFile, generateRulesContent(agentList));
39536
+ const rulesDir = join49(root, env.rulesDir);
39537
+ const rulesFile = join49(root, env.rulesFile);
39538
+ mkdirSync22(rulesDir, { recursive: true });
39539
+ writeFileSync18(rulesFile, generateRulesContent(agentList));
39188
39540
  const lines = [`Host: ${env.host}`, ""];
39189
39541
  if (nativeCreated.length > 0) {
39190
39542
  lines.push(`Native agents created (${nativeCreated.length}):`);
@@ -39890,9 +40242,11 @@ server.tool(
39890
40242
  skills: external_exports.array(external_exports.object({
39891
40243
  name: external_exports.string().describe("Skill name (kebab-case)"),
39892
40244
  content: external_exports.string().describe("Full .md content with frontmatter")
39893
- })).optional().describe("For build: generated skill files to save. Omit for discovery mode.")
40245
+ })).optional().describe("For build: generated skill files to save. Omit for discovery mode."),
40246
+ // Internal: re-entry param for native-utility dispatch path (develop action only)
40247
+ _utility_task_id: external_exports.string().optional().describe("Internal: utility task ID for re-entry after native skill generation")
39894
40248
  },
39895
- async ({ action, agent_id, skill, enabled, category, skill_names, skills }) => {
40249
+ async ({ action, agent_id, skill, enabled, category, skill_names, skills, _utility_task_id }) => {
39896
40250
  await boot();
39897
40251
  if (action === "list") {
39898
40252
  const index = ctx.mainAgent.getSkillIndex();
@@ -39948,6 +40302,98 @@ ${sections.join("\n\n")}` }] };
39948
40302
  if (!ctx.skillEngine) {
39949
40303
  return { content: [{ type: "text", text: "Skill engine not available. Check boot logs." }] };
39950
40304
  }
40305
+ if (_utility_task_id) {
40306
+ const stashedMeta = _pendingSkillData.get(_utility_task_id);
40307
+ _pendingSkillData.delete(_utility_task_id);
40308
+ const utilityResult = ctx.nativeResultMap.get(_utility_task_id);
40309
+ ctx.nativeResultMap.delete(_utility_task_id);
40310
+ ctx.nativeTaskMap.delete(_utility_task_id);
40311
+ if (utilityResult?.status === "completed" && utilityResult.result && stashedMeta) {
40312
+ try {
40313
+ const result = ctx.skillEngine.saveFromRaw(agent_id, category, utilityResult.result, stashedMeta);
40314
+ const { normalizeSkillName: nsn } = await Promise.resolve().then(() => (init_src4(), src_exports3));
40315
+ const skillName = nsn(category);
40316
+ if (ctx.mainAgent) {
40317
+ const skillIndex = ctx.mainAgent.getSkillIndex();
40318
+ if (skillIndex) {
40319
+ skillIndex.bind(agent_id, skillName, { source: "auto", mode: "permanent" });
40320
+ }
40321
+ const registry2 = ctx.mainAgent.registry;
40322
+ const agentConfig = registry2?.get(agent_id);
40323
+ const normalizedCategory = nsn(category);
40324
+ if (agentConfig && !agentConfig.skills.includes(normalizedCategory)) {
40325
+ agentConfig.skills.push(normalizedCategory);
40326
+ }
40327
+ const pipeline = ctx.mainAgent.pipeline;
40328
+ if (pipeline?.suppressSkillGapAlert) {
40329
+ pipeline.suppressSkillGapAlert(agent_id, category);
40330
+ }
40331
+ }
40332
+ const preview = result.content.length > 1e3 ? result.content.slice(0, 1e3) + "\n\n... (truncated)" : result.content;
40333
+ process.stderr.write(`[gossipcat] Skill developed (native): "${skillName}" for ${agent_id} (category: ${category})
40334
+ `);
40335
+ return {
40336
+ content: [{ type: "text", text: `Skill generated and saved:
40337
+
40338
+ Path: ${result.path}
40339
+
40340
+ Auto-bound "${skillName}" to ${agent_id} in skill index.
40341
+
40342
+ ${preview}` }]
40343
+ };
40344
+ } catch (err) {
40345
+ process.stderr.write(`[gossipcat] Skill develop native post-processing failed: ${err.message}
40346
+ `);
40347
+ return {
40348
+ content: [{ type: "text", text: `Skill generation failed: ${err.message}` }]
40349
+ };
40350
+ }
40351
+ } else {
40352
+ process.stderr.write(`[gossipcat] Skill develop utility ${_utility_task_id} failed/missing, falling back to direct LLM
40353
+ `);
40354
+ }
40355
+ }
40356
+ if (ctx.nativeUtilityConfig && !_utility_task_id) {
40357
+ try {
40358
+ const { system, user, skillName, skillPath, baseline_accuracy_correct, baseline_accuracy_hallucinated, bound_at } = await ctx.skillEngine.buildPrompt(agent_id, category);
40359
+ const taskId = (0, import_crypto17.randomUUID)().slice(0, 8);
40360
+ _pendingSkillData.set(taskId, { agentId: agent_id, category, skillName, skillPath, baseline_accuracy_correct, baseline_accuracy_hallucinated, bound_at });
40361
+ ctx.nativeTaskMap.set(taskId, {
40362
+ agentId: "_utility",
40363
+ task: `skill_develop:${category}`,
40364
+ startedAt: Date.now(),
40365
+ timeoutMs: 12e4,
40366
+ utilityType: "skill_develop"
40367
+ });
40368
+ spawnTimeoutWatcher(taskId, ctx.nativeTaskMap.get(taskId));
40369
+ const modelShort = ctx.nativeUtilityConfig.model;
40370
+ return {
40371
+ content: [
40372
+ {
40373
+ type: "text",
40374
+ text: `Skill prompt built. Dispatching native utility for generation.
40375
+
40376
+ \u26A0\uFE0F EXECUTE NOW \u2014 launch this Agent and re-call gossip_skills:
40377
+
40378
+ 1. Agent(model: "${modelShort}", prompt: <AGENT_PROMPT:${taskId} below>, run_in_background: true) \u2014 pass the AGENT_PROMPT:${taskId} content item verbatim
40379
+ 2. When agent completes \u2192 gossip_relay(task_id: "${taskId}", result: "<full agent output>")
40380
+ 3. Then re-call: gossip_skills(action: "develop", agent_id: "${agent_id}", category: "${category}", _utility_task_id: "${taskId}")
40381
+
40382
+ Do ALL steps in order. Do not wait for user input between them.`
40383
+ },
40384
+ { type: "text", text: `AGENT_PROMPT:${taskId} (_utility)
40385
+ ${system}
40386
+
40387
+ ---
40388
+
40389
+ ${user}` }
40390
+ ]
40391
+ };
40392
+ } catch (err) {
40393
+ process.stderr.write(`[gossipcat] Skill develop native prompt build failed: ${err.message}, falling back to direct LLM
40394
+ `);
40395
+ }
40396
+ }
39951
40397
  try {
39952
40398
  const result = await ctx.skillEngine.generate(agent_id, category);
39953
40399
  const { normalizeSkillName: nsn } = await Promise.resolve().then(() => (init_src4(), src_exports3));
@@ -39991,16 +40437,16 @@ ${preview}` }]
39991
40437
  const { SkillGapTracker: SkillGapTracker2, parseSkillFrontmatter: parseSkillFrontmatter2, normalizeSkillName: normalizeSkillName2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
39992
40438
  const tracker = new SkillGapTracker2(process.cwd());
39993
40439
  if (skills && skills.length > 0) {
39994
- const { writeFileSync: writeFileSync17, mkdirSync: mkdirSync21, existsSync: existsSync41, readFileSync: readFileSync40 } = require("fs");
39995
- const { join: join48 } = require("path");
39996
- const dir = join48(process.cwd(), ".gossip", "skills");
39997
- mkdirSync21(dir, { recursive: true });
40440
+ const { writeFileSync: writeFileSync18, mkdirSync: mkdirSync22, existsSync: existsSync44, readFileSync: readFileSync41 } = require("fs");
40441
+ const { join: join49 } = require("path");
40442
+ const dir = join49(process.cwd(), ".gossip", "skills");
40443
+ mkdirSync22(dir, { recursive: true });
39998
40444
  const results = [];
39999
40445
  for (const sk of skills) {
40000
40446
  const name = normalizeSkillName2(sk.name);
40001
- const filePath = join48(dir, `${name}.md`);
40002
- if (existsSync41(filePath)) {
40003
- const existing = readFileSync40(filePath, "utf-8");
40447
+ const filePath = join49(dir, `${name}.md`);
40448
+ if (existsSync44(filePath)) {
40449
+ const existing = readFileSync41(filePath, "utf-8");
40004
40450
  const fm = parseSkillFrontmatter2(existing);
40005
40451
  if (fm) {
40006
40452
  if (fm.generated_by === "manual") {
@@ -40017,7 +40463,7 @@ ${preview}` }]
40017
40463
  }
40018
40464
  }
40019
40465
  }
40020
- writeFileSync17(filePath, sk.content);
40466
+ writeFileSync18(filePath, sk.content);
40021
40467
  tracker.recordResolution(name);
40022
40468
  results.push(`Created .gossip/skills/${name}.md`);
40023
40469
  }
@@ -40061,9 +40507,10 @@ server.tool(
40061
40507
  "Save a cognitive session summary to project memory. The next session will load this context automatically on MCP connect. Call before ending your session to preserve what was learned.",
40062
40508
  {
40063
40509
  notes: external_exports.string().optional().describe('Optional freeform user context (e.g., "focusing on security hardening")'),
40510
+ force: external_exports.boolean().optional().describe("Bypass the refuse-gate for pending native tasks / consensus rounds. Audited to .gossip/forced-saves.jsonl."),
40064
40511
  _utility_task_id: external_exports.string().optional().describe("Internal: utility task ID for re-entry after native session summary")
40065
40512
  },
40066
- async ({ notes, _utility_task_id }) => {
40513
+ async ({ notes, force, _utility_task_id }) => {
40067
40514
  await boot();
40068
40515
  if (_utility_task_id) {
40069
40516
  const stashed = _pendingSessionData.get(_utility_task_id);
@@ -40164,6 +40611,48 @@ server.tool(
40164
40611
 
40165
40612
  ${summary2}` }] };
40166
40613
  }
40614
+ {
40615
+ const pendingNative = [];
40616
+ for (const [taskId, info] of ctx.nativeTaskMap) {
40617
+ if (!ctx.nativeResultMap.has(taskId)) pendingNative.push(`${taskId} (${info.agentId})`);
40618
+ }
40619
+ const pendingConsensus = [];
40620
+ const now = Date.now();
40621
+ for (const [cid, round] of ctx.pendingConsensusRounds) {
40622
+ if (round.deadline && now > round.deadline) continue;
40623
+ if (round.pendingNativeAgents && round.pendingNativeAgents.size > 0) {
40624
+ pendingConsensus.push(`${cid} (${round.pendingNativeAgents.size} agents)`);
40625
+ }
40626
+ }
40627
+ if ((pendingNative.length > 0 || pendingConsensus.length > 0) && !force) {
40628
+ const lines = ["\u26D4 session_save refused \u2014 work still in flight.\n"];
40629
+ if (pendingNative.length > 0) {
40630
+ lines.push(`Pending native tasks (${pendingNative.length}):`);
40631
+ for (const t of pendingNative.slice(0, 10)) lines.push(` - ${t}`);
40632
+ lines.push("");
40633
+ }
40634
+ if (pendingConsensus.length > 0) {
40635
+ lines.push(`Pending consensus rounds (${pendingConsensus.length}):`);
40636
+ for (const c of pendingConsensus.slice(0, 10)) lines.push(` - ${c}`);
40637
+ lines.push("");
40638
+ }
40639
+ lines.push("Relay the results via gossip_relay / gossip_relay_cross_review, then re-call gossip_session_save.");
40640
+ lines.push("To override (snapshot is intentional / tasks are abandoned): gossip_session_save(force: true).");
40641
+ return { content: [{ type: "text", text: lines.join("\n") }], isError: true };
40642
+ }
40643
+ if (force && (pendingNative.length > 0 || pendingConsensus.length > 0)) {
40644
+ try {
40645
+ const { appendFileSync: af, mkdirSync: md } = require("fs");
40646
+ const { join: j } = require("path");
40647
+ md(j(process.cwd(), ".gossip"), { recursive: true });
40648
+ af(
40649
+ j(process.cwd(), ".gossip", "forced-saves.jsonl"),
40650
+ JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), pendingNative, pendingConsensus, notes: notes || null }) + "\n"
40651
+ );
40652
+ } catch {
40653
+ }
40654
+ }
40655
+ }
40167
40656
  let gossipText = "";
40168
40657
  try {
40169
40658
  const { existsSync: ex, readFileSync: rf } = require("fs");
@@ -40533,6 +41022,7 @@ server.tool(
40533
41022
  { name: "gossip_remember", desc: "Search an agent's archived knowledge files by keyword query." },
40534
41023
  { name: "gossip_verify_memory", desc: "On-demand staleness check for a memory file claim. Returns FRESH | STALE | CONTRADICTED | INCONCLUSIVE with file:line evidence." },
40535
41024
  { name: "gossip_tools", desc: "List available tools (this command)." },
41025
+ { name: "gossip_guide", desc: "Show the gossipcat handbook for humans \u2014 invariants, operator playbook, caveats, hallucination patterns, glossary. Read the docs, not LLM context." },
40536
41026
  { name: "gossip_progress", desc: "Show active task progress and consensus phase. No params." },
40537
41027
  { name: "gossip_format", desc: "Return the CONSENSUS_OUTPUT_FORMAT block to paste into ad-hoc Agent() prompts so native subagents emit parseable <agent_finding> tags." },
40538
41028
  { name: "gossip_bug_feedback", desc: "File a GitHub issue on the gossipcat repo from an in-session bug report. Dedupes against open issues." }
@@ -40685,14 +41175,8 @@ ${match.url}` }] };
40685
41175
  }
40686
41176
  return { content: [{ type: "text", text: `Dedup check failed (${err.message ?? "unknown error"}). Not filing to avoid duplicates. Run \`gh auth status\` and retry.` }] };
40687
41177
  }
40688
- let version2 = "unknown";
40689
- try {
40690
- const { readFileSync: readFileSync40 } = await import("fs");
40691
- const { join: join48 } = await import("path");
40692
- const pkgRaw = readFileSync40(join48(process.cwd(), "package.json"), "utf-8");
40693
- version2 = JSON.parse(pkgRaw).version ?? "unknown";
40694
- } catch {
40695
- }
41178
+ const { getGossipcatVersion: getGossipcatVersion2 } = await Promise.resolve().then(() => (init_version(), version_exports));
41179
+ const version2 = getGossipcatVersion2();
40696
41180
  let gitHead = "unknown";
40697
41181
  try {
40698
41182
  const result = await execFile4("git", ["rev-parse", "--short", "HEAD"]);
@@ -40750,8 +41234,10 @@ function touchSession(sid) {
40750
41234
  `);
40751
41235
  }, HTTP_SESSION_TTL_MS);
40752
41236
  }
40753
- function startHttpMcpTransport() {
40754
- const port = parseInt(process.env.GOSSIPCAT_HTTP_PORT ?? "24421", 10);
41237
+ async function startHttpMcpTransport() {
41238
+ const httpPick = await pickStickyPort("GOSSIPCAT_HTTP_PORT", HTTP_MCP_STICKY_FILE);
41239
+ const port = httpPick.source === "auto" ? 24421 : httpPick.port;
41240
+ ctx.httpMcpPortSource = httpPick.source;
40755
41241
  const token = process.env.GOSSIPCAT_HTTP_TOKEN ?? "";
40756
41242
  const bindHost = process.env.GOSSIPCAT_HTTP_BIND === "0.0.0.0" && token ? "0.0.0.0" : "127.0.0.1";
40757
41243
  const tokenBuf = token ? Buffer.from(token) : null;
@@ -40774,10 +41260,10 @@ function startHttpMcpTransport() {
40774
41260
  }
40775
41261
  let body;
40776
41262
  if (req.method === "POST") {
40777
- const raw = await new Promise((resolve17, reject) => {
41263
+ const raw = await new Promise((resolve18, reject) => {
40778
41264
  const chunks = [];
40779
41265
  req.on("data", (c) => chunks.push(c));
40780
- req.on("end", () => resolve17(Buffer.concat(chunks).toString("utf-8")));
41266
+ req.on("end", () => resolve18(Buffer.concat(chunks).toString("utf-8")));
40781
41267
  req.on("error", reject);
40782
41268
  });
40783
41269
  try {
@@ -40845,9 +41331,13 @@ function startHttpMcpTransport() {
40845
41331
  res.end(JSON.stringify({ error: "Bad request" }));
40846
41332
  });
40847
41333
  httpServer.listen(port, bindHost, () => {
41334
+ const addr = httpServer.address();
41335
+ const bound = typeof addr === "object" && addr ? addr.port : port;
41336
+ ctx.httpMcpPort = bound;
41337
+ if (bound > 0) writeStickyPort(HTTP_MCP_STICKY_FILE, bound);
40848
41338
  const authNote = token ? " (token protected)" : " (no auth \u2014 set GOSSIPCAT_HTTP_TOKEN to secure)";
40849
41339
  const bindNote = bindHost === "0.0.0.0" ? " [remote]" : " [localhost only]";
40850
- process.stderr.write(`[gossipcat] HTTP MCP listening on :${port}/mcp${authNote}${bindNote}
41340
+ process.stderr.write(`[gossipcat] HTTP MCP listening on :${bound}/mcp${authNote}${bindNote}
40851
41341
  `);
40852
41342
  });
40853
41343
  httpServer.on("error", (err) => {