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