gossipcat 0.6.7 → 0.6.9

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.
@@ -6967,11 +6967,14 @@ var init_mcp_context = __esm({
6967
6967
  // packages/orchestrator/src/log.ts
6968
6968
  function ts() {
6969
6969
  const d = /* @__PURE__ */ new Date();
6970
+ const yyyy = d.getUTCFullYear();
6971
+ const mo = String(d.getUTCMonth() + 1).padStart(2, "0");
6972
+ const dd = String(d.getUTCDate()).padStart(2, "0");
6970
6973
  const hh = String(d.getUTCHours()).padStart(2, "0");
6971
6974
  const mm = String(d.getUTCMinutes()).padStart(2, "0");
6972
6975
  const ss = String(d.getUTCSeconds()).padStart(2, "0");
6973
6976
  const ms = String(d.getUTCMilliseconds()).padStart(3, "0");
6974
- return `${hh}:${mm}:${ss}.${ms}`;
6977
+ return `${yyyy}-${mo}-${dd} ${hh}:${mm}:${ss}.${ms}Z`;
6975
6978
  }
6976
6979
  function emojiFor(tag) {
6977
6980
  if (TAG_EMOJI[tag]) return TAG_EMOJI[tag];
@@ -14617,13 +14620,17 @@ ${context}` : ""}
14617
14620
  });
14618
14621
 
14619
14622
  // packages/tools/src/file-tools.ts
14620
- var import_promises, import_path6, import_fs7, FileTools;
14623
+ var import_promises, import_path6, import_fs7, import_re2, MAX_GREP_FILE_BYTES, MAX_GREP_MATCHES, MAX_GREP_FILES, FileTools;
14621
14624
  var init_file_tools = __esm({
14622
14625
  "packages/tools/src/file-tools.ts"() {
14623
14626
  "use strict";
14624
14627
  import_promises = require("fs/promises");
14625
14628
  import_path6 = require("path");
14626
14629
  import_fs7 = require("fs");
14630
+ import_re2 = __toESM(require("re2"));
14631
+ MAX_GREP_FILE_BYTES = 2 * 1024 * 1024;
14632
+ MAX_GREP_MATCHES = 2e3;
14633
+ MAX_GREP_FILES = 5e3;
14627
14634
  FileTools = class {
14628
14635
  constructor(sandbox) {
14629
14636
  this.sandbox = sandbox;
@@ -14709,13 +14716,17 @@ var init_file_tools = __esm({
14709
14716
  const searchRoot = args.path ? this.sandbox.validatePath(args.path, allowed) : agentRoot || this.sandbox.projectRoot;
14710
14717
  let regex;
14711
14718
  try {
14712
- regex = new RegExp(args.pattern);
14719
+ regex = new import_re2.default(args.pattern);
14713
14720
  } catch (error51) {
14714
14721
  return `Invalid regex pattern: ${error51 instanceof Error ? error51.message : "Unknown error"}`;
14715
14722
  }
14716
- const results = [];
14717
- await this.grepDir(searchRoot, regex, results, 0, 10);
14718
- return results.join("\n") || "No matches found";
14723
+ const state = { matches: [], filesScanned: 0, truncated: false };
14724
+ await this.grepDir(searchRoot, regex, state, 0, 10);
14725
+ let output = state.matches.join("\n") || "No matches found";
14726
+ if (state.truncated) {
14727
+ output += "\n... (truncated \u2014 result or scan limit reached; narrow your pattern/path)";
14728
+ }
14729
+ return output;
14719
14730
  }
14720
14731
  async fileTree(args, agentRoot) {
14721
14732
  const allowed = agentRoot ? [agentRoot] : [];
@@ -14735,7 +14746,7 @@ var init_file_tools = __esm({
14735
14746
  return;
14736
14747
  }
14737
14748
  const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
14738
- const regex = new RegExp(regexStr);
14749
+ const regex = new import_re2.default(regexStr);
14739
14750
  for (const entry of entries) {
14740
14751
  if (entry === "node_modules" || entry === ".git") continue;
14741
14752
  const fullPath = (0, import_path6.join)(dir, entry);
@@ -14755,8 +14766,9 @@ var init_file_tools = __esm({
14755
14766
  }
14756
14767
  }
14757
14768
  }
14758
- async grepDir(dir, regex, results, depth = 0, maxDepth = 10) {
14769
+ async grepDir(dir, regex, state, depth = 0, maxDepth = 10) {
14759
14770
  if (depth >= maxDepth) return;
14771
+ if (state.truncated) return;
14760
14772
  let entries;
14761
14773
  try {
14762
14774
  entries = await (0, import_promises.readdir)(dir);
@@ -14764,6 +14776,7 @@ var init_file_tools = __esm({
14764
14776
  return;
14765
14777
  }
14766
14778
  for (const entry of entries) {
14779
+ if (state.truncated) return;
14767
14780
  if (entry === "node_modules" || entry === ".git") continue;
14768
14781
  const fullPath = (0, import_path6.join)(dir, entry);
14769
14782
  let info;
@@ -14773,17 +14786,29 @@ var init_file_tools = __esm({
14773
14786
  continue;
14774
14787
  }
14775
14788
  if (info.isDirectory()) {
14776
- await this.grepDir(fullPath, regex, results, depth + 1, maxDepth);
14789
+ await this.grepDir(fullPath, regex, state, depth + 1, maxDepth);
14777
14790
  } else {
14791
+ if (info.size > MAX_GREP_FILE_BYTES) {
14792
+ continue;
14793
+ }
14794
+ state.filesScanned++;
14795
+ if (state.filesScanned > MAX_GREP_FILES) {
14796
+ state.truncated = true;
14797
+ return;
14798
+ }
14778
14799
  try {
14779
14800
  const content = await (0, import_promises.readFile)(fullPath, "utf-8");
14780
14801
  const lines = content.split("\n");
14781
14802
  const relPath = (0, import_path6.relative)(this.sandbox.projectRoot, fullPath);
14782
- lines.forEach((line, idx) => {
14783
- if (regex.test(line)) {
14784
- results.push(`${relPath}:${idx + 1}: ${line}`);
14803
+ for (let idx = 0; idx < lines.length; idx++) {
14804
+ if (state.matches.length >= MAX_GREP_MATCHES) {
14805
+ state.truncated = true;
14806
+ break;
14785
14807
  }
14786
- });
14808
+ if (regex.test(lines[idx])) {
14809
+ state.matches.push(`${relPath}:${idx + 1}: ${lines[idx]}`);
14810
+ }
14811
+ }
14787
14812
  } catch {
14788
14813
  }
14789
14814
  }
@@ -20216,6 +20241,9 @@ __export(src_exports2, {
20216
20241
  GIT_TOOLS: () => GIT_TOOLS,
20217
20242
  GitTools: () => GitTools,
20218
20243
  IDENTITY_TOOLS: () => IDENTITY_TOOLS,
20244
+ MAX_GREP_FILES: () => MAX_GREP_FILES,
20245
+ MAX_GREP_FILE_BYTES: () => MAX_GREP_FILE_BYTES,
20246
+ MAX_GREP_MATCHES: () => MAX_GREP_MATCHES,
20219
20247
  MAX_MEMORY_QUERY_LOG_BYTES: () => MAX_MEMORY_QUERY_LOG_BYTES,
20220
20248
  MEMORY_QUERY_LOG: () => MEMORY_QUERY_LOG,
20221
20249
  MEMORY_TOOLS: () => MEMORY_TOOLS,
@@ -24732,7 +24760,7 @@ This section will be used for cross-review with peer agents.
24732
24760
 
24733
24761
  ${OUTPUT_DELIVERY_PROTOCOL}` });
24734
24762
  } else {
24735
- const hasAnyMeaningfulPart = !!(parts.identity || parts.instructions || parts.memory || parts.memoryDir || parts.lens || parts.skills || parts.context || parts.sessionContext || parts.chainContext || parts.specReviewContext || parts.projectStructure || parts.task || parts.consensusFindings && parts.consensusFindings.length > 0);
24763
+ const hasAnyMeaningfulPart = !!(parts.identity || parts.instructions || parts.memory || parts.memoryDir || parts.lens || parts.skills || parts.context || parts.sessionContext || parts.chainContext || parts.specReviewContext || parts.projectStructure || parts.task || parts.consensusFindings && parts.consensusFindings.length > 0 || parts.agentCorrections && parts.agentCorrections.length > 0);
24736
24764
  if (hasAnyMeaningfulPart) {
24737
24765
  suffix.push({ priority: 0, text: `
24738
24766
 
@@ -24758,13 +24786,18 @@ ${parts.lens}
24758
24786
  ${parts.specReviewContext}
24759
24787
  --- END SPEC REVIEW ---` });
24760
24788
  }
24761
- if (parts.memory || parts.consensusFindings && parts.consensusFindings.length > 0) {
24789
+ if (parts.memory || parts.consensusFindings && parts.consensusFindings.length > 0 || parts.agentCorrections && parts.agentCorrections.length > 0) {
24762
24790
  const memParts = [];
24763
24791
  if (parts.memory) memParts.push(parts.memory);
24764
24792
  if (parts.consensusFindings && parts.consensusFindings.length > 0) {
24765
24793
  const findingsBlock = "### Recent Consensus Findings\n" + parts.consensusFindings.map((f, i) => `${i + 1}. ${f}`).join("\n");
24766
24794
  memParts.push(findingsBlock);
24767
24795
  }
24796
+ if (parts.agentCorrections && parts.agentCorrections.length > 0) {
24797
+ memParts.push(
24798
+ "### Your Prior Corrections\n" + parts.agentCorrections.map((f, i) => `${i + 1}. ${f}`).join("\n")
24799
+ );
24800
+ }
24768
24801
  suffix.push({ priority: 3, text: `
24769
24802
 
24770
24803
  --- MEMORY ---
@@ -24883,7 +24916,7 @@ var init_prompt_assembler = __esm({
24883
24916
  });
24884
24917
 
24885
24918
  // packages/orchestrator/src/agent-memory.ts
24886
- var import_fs20, import_path21, FINDINGS_MAX_RESULTS, FINDINGS_MAX_CHARS, FINDINGS_STALE_DAYS, FINDINGS_MIN_SCORE, AgentMemoryReader;
24919
+ var import_fs20, import_path21, FINDINGS_MAX_RESULTS, FINDINGS_MAX_CHARS, FINDINGS_STALE_DAYS, FINDINGS_MIN_SCORE, CORRECTIONS_MAX_RESULTS, AgentMemoryReader;
24887
24920
  var init_agent_memory = __esm({
24888
24921
  "packages/orchestrator/src/agent-memory.ts"() {
24889
24922
  "use strict";
@@ -24893,6 +24926,7 @@ var init_agent_memory = __esm({
24893
24926
  FINDINGS_MAX_CHARS = 150;
24894
24927
  FINDINGS_STALE_DAYS = 30;
24895
24928
  FINDINGS_MIN_SCORE = 1;
24929
+ CORRECTIONS_MAX_RESULTS = 2;
24896
24930
  AgentMemoryReader = class {
24897
24931
  constructor(projectRoot) {
24898
24932
  this.projectRoot = projectRoot;
@@ -24993,6 +25027,46 @@ ${content}
24993
25027
  }
24994
25028
  return scored.sort((a, b) => b.score - a.score).slice(0, FINDINGS_MAX_RESULTS).map((s) => s.text);
24995
25029
  }
25030
+ /**
25031
+ * Pre-fetch this agent's own prior lesson cards relevant to the task (issue #642 B-delta).
25032
+ * Selects by `lesson-*.md` filename (not frontmatter `type`, which parsers don't read),
25033
+ * ranks by body keyword overlap, drops cards older than FINDINGS_STALE_DAYS, and strips
25034
+ * prompt-injection delimiters before returning. Returns top CORRECTIONS_MAX_RESULTS snippets.
25035
+ */
25036
+ prefetchAgentCorrectionsText(agentId, taskText) {
25037
+ if (!agentId || /[/\\.\0]/.test(agentId)) return [];
25038
+ const knowledgeDir = (0, import_path21.join)(this.projectRoot, ".gossip", "agents", agentId, "memory", "knowledge");
25039
+ if (!(0, import_fs20.existsSync)(knowledgeDir)) return [];
25040
+ let files;
25041
+ try {
25042
+ files = (0, import_fs20.readdirSync)(knowledgeDir).filter((f) => f.startsWith("lesson-") && f.endsWith(".md"));
25043
+ } catch {
25044
+ return [];
25045
+ }
25046
+ if (files.length === 0) return [];
25047
+ const keywords = this.extractKeywords(taskText);
25048
+ if (keywords.length === 0) return [];
25049
+ const cutoffMs = Date.now() - FINDINGS_STALE_DAYS * 864e5;
25050
+ const scored = [];
25051
+ for (const file2 of files) {
25052
+ const filePath = (0, import_path21.join)(knowledgeDir, file2);
25053
+ let content;
25054
+ try {
25055
+ const mtime = (0, import_fs20.statSync)(filePath).mtimeMs;
25056
+ if (mtime < cutoffMs) continue;
25057
+ content = (0, import_fs20.readFileSync)(filePath, "utf-8");
25058
+ } catch {
25059
+ continue;
25060
+ }
25061
+ const body = content.replace(/^---\n[\s\S]*?\n---\n*/, "").replace(/<\/?(?:agent-memory|system|instructions)>/gi, "").replace(/\s+/g, " ").trim();
25062
+ if (!body) continue;
25063
+ const score = this.scoreKeywords(keywords, body);
25064
+ if (score >= FINDINGS_MIN_SCORE) {
25065
+ scored.push({ text: body.slice(0, FINDINGS_MAX_CHARS).trim(), score });
25066
+ }
25067
+ }
25068
+ return scored.sort((a, b) => b.score - a.score).slice(0, CORRECTIONS_MAX_RESULTS).map((s) => s.text);
25069
+ }
24996
25070
  /** Simple word-boundary keyword overlap scoring (no LLM). */
24997
25071
  extractKeywords(taskText) {
24998
25072
  const words = taskText.toLowerCase().split(/[\s,/.;:!?()\[\]{}*_~`"'<>|]+/);
@@ -25477,7 +25551,21 @@ function validateAgentId(agentId) {
25477
25551
  function sanitizeTaskId(taskId) {
25478
25552
  return taskId.replace(/[/\\:*?"<>|\0]/g, "_");
25479
25553
  }
25480
- var import_fs23, import_path24, import_crypto11, MemoryWriter;
25554
+ function writeLessonCardsForSignals(projectRoot, signals) {
25555
+ if (!Array.isArray(signals) || signals.length === 0) return;
25556
+ const writer = new MemoryWriter(projectRoot);
25557
+ for (const s of signals) {
25558
+ if (!s || !s.finding_id || !TERMINAL_LESSON_SIGNALS.has(s.signal)) continue;
25559
+ writer.writeLessonCard(s.agent_id, {
25560
+ signal: s.signal,
25561
+ findingId: s.finding_id,
25562
+ finding: s.finding ?? "",
25563
+ lesson: s.lesson,
25564
+ taskTokens: s.finding
25565
+ });
25566
+ }
25567
+ }
25568
+ var import_fs23, import_path24, import_crypto11, TERMINAL_LESSON_SIGNALS, LESSON_CARDS_MAX_PER_AGENT, LESSON_RESERVED_AGENT_IDS, MemoryWriter;
25481
25569
  var init_memory_writer = __esm({
25482
25570
  "packages/orchestrator/src/memory-writer.ts"() {
25483
25571
  "use strict";
@@ -25488,6 +25576,13 @@ var init_memory_writer = __esm({
25488
25576
  init_project_structure();
25489
25577
  init_log();
25490
25578
  init_completion_signals();
25579
+ TERMINAL_LESSON_SIGNALS = /* @__PURE__ */ new Set([
25580
+ "hallucination_caught",
25581
+ "impl_test_fail",
25582
+ "impl_peer_rejected"
25583
+ ]);
25584
+ LESSON_CARDS_MAX_PER_AGENT = 200;
25585
+ LESSON_RESERVED_AGENT_IDS = /* @__PURE__ */ new Set(["_project", "_system", "_utility"]);
25491
25586
  MemoryWriter = class {
25492
25587
  constructor(projectRoot) {
25493
25588
  this.projectRoot = projectRoot;
@@ -26060,7 +26155,7 @@ ${truncateAtWord(summaryBody, NEXT_SESSION_MAX_CHARS)}
26060
26155
  /** Shared warmth-aware pruning — evicts lowest-warmth files, respects pinned */
26061
26156
  pruneKnowledgeDir(knowledgeDir, maxFiles) {
26062
26157
  try {
26063
- const existing = (0, import_fs23.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md")).sort();
26158
+ const existing = (0, import_fs23.readdirSync)(knowledgeDir).filter((f) => f.endsWith(".md") && !f.endsWith("-session.md") && !f.startsWith("lesson-")).sort();
26064
26159
  if (existing.length >= maxFiles) {
26065
26160
  const scored = existing.map((f) => {
26066
26161
  const content = (0, import_fs23.readFileSync)((0, import_path24.join)(knowledgeDir, f), "utf-8");
@@ -26364,6 +26459,76 @@ ${result}`;
26364
26459
  this.pruneKnowledgeDir(knowledgeDir, 25);
26365
26460
  (0, import_fs23.writeFileSync)((0, import_path24.join)(knowledgeDir, filename), content);
26366
26461
  }
26462
+ /**
26463
+ * Write a recallable "lesson card" for a terminal correction signal (issue #642 A).
26464
+ * Best-effort: any failure is swallowed — this MUST NOT fail signal recording.
26465
+ * Idempotent: keyed by finding_id via sanitizeTaskId, no timestamp in the filename.
26466
+ */
26467
+ writeLessonCard(agentId, card) {
26468
+ try {
26469
+ if (LESSON_RESERVED_AGENT_IDS.has(agentId)) return;
26470
+ validateAgentId(agentId);
26471
+ const slug = sanitizeTaskId(card.findingId).slice(0, 96);
26472
+ if (!slug) return;
26473
+ const memDir = this.ensureDirs(agentId);
26474
+ const knowledgeDir = (0, import_path24.join)(memDir, "knowledge");
26475
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
26476
+ const generated = (/* @__PURE__ */ new Date()).toISOString();
26477
+ const shortId = slug.slice(0, 24);
26478
+ const finding = truncateAtWord(sanitizeYamlValue(card.finding ?? ""), 400);
26479
+ const lessonBody = card.lesson ? truncateAtWord(sanitizeYamlValue(card.lesson), 400) : "(no root-cause supplied)";
26480
+ const desc = sanitizeYamlValue(card.lesson || card.finding || "lesson").slice(0, 80);
26481
+ const taskTokens = (card.taskTokens ?? card.finding ?? "").replace(/[\n\r]/g, " ").slice(0, 300);
26482
+ const content = [
26483
+ "---",
26484
+ `name: lesson-${sanitizeYamlValue(card.signal)}-${shortId}`,
26485
+ `description: ${desc}`,
26486
+ "type: lesson",
26487
+ `agent: ${sanitizeYamlValue(agentId)}`,
26488
+ `signal: ${sanitizeYamlValue(card.signal)}`,
26489
+ `finding_id: ${sanitizeYamlValue(card.findingId)}`,
26490
+ "importance: 0.7",
26491
+ `lastAccessed: ${today}`,
26492
+ "accessCount: 0",
26493
+ `generated: ${generated}`,
26494
+ "---",
26495
+ "",
26496
+ `**Why it failed:** ${lessonBody}`,
26497
+ "",
26498
+ `**What happened:** ${finding}`,
26499
+ "",
26500
+ `**Task context:** ${taskTokens}`
26501
+ ].join("\n");
26502
+ const cardPath = (0, import_path24.join)(knowledgeDir, `lesson-${slug}.md`);
26503
+ if (!(0, import_fs23.existsSync)(cardPath)) this.pruneLessonCards(knowledgeDir, LESSON_CARDS_MAX_PER_AGENT);
26504
+ (0, import_fs23.writeFileSync)(cardPath, content);
26505
+ } catch {
26506
+ }
26507
+ }
26508
+ /** Count-based prune of lesson-*.md only (leaves consensus/session knowledge untouched). */
26509
+ pruneLessonCards(knowledgeDir, maxFiles) {
26510
+ try {
26511
+ const cards = (0, import_fs23.readdirSync)(knowledgeDir).filter((f) => f.startsWith("lesson-") && f.endsWith(".md"));
26512
+ if (cards.length < maxFiles) return;
26513
+ const withMtime = cards.map((f) => {
26514
+ let mtime = 0;
26515
+ try {
26516
+ mtime = (0, import_fs23.statSync)((0, import_path24.join)(knowledgeDir, f)).mtimeMs;
26517
+ } catch {
26518
+ }
26519
+ return { f, mtime };
26520
+ });
26521
+ withMtime.sort((a, b) => a.mtime - b.mtime);
26522
+ const toEvict = withMtime.slice(0, cards.length - (maxFiles - 1));
26523
+ for (const item of toEvict) {
26524
+ try {
26525
+ (0, import_fs23.unlinkSync)((0, import_path24.join)(knowledgeDir, item.f));
26526
+ } catch {
26527
+ }
26528
+ }
26529
+ } catch {
26530
+ }
26531
+ }
26367
26532
  /**
26368
26533
  * Update task memory importance based on consensus signals.
26369
26534
  * High-quality findings get boosted, hallucinations get reduced.
@@ -32270,6 +32435,10 @@ var init_dispatch_pipeline = __esm({
32270
32435
  if (consensusFindings.length > 0) {
32271
32436
  gossipLog(`\u{1F4CB} pre-fetched ${consensusFindings.length} consensus findings for ${agentId}`);
32272
32437
  }
32438
+ const agentCorrections = this.memReader.prefetchAgentCorrectionsText(agentId, task);
32439
+ if (agentCorrections.length > 0) {
32440
+ gossipLog(`\u{1F9E0} pre-fetched ${agentCorrections.length} prior corrections for ${agentId}`);
32441
+ }
32273
32442
  const effectiveSkills = resolveEffectiveSkills(agentId, agentSkills, this.skillIndex ?? void 0);
32274
32443
  const skillWarnings = this.catalog ? this.catalog.checkCoverage(effectiveSkills, task) : [];
32275
32444
  const sessionGossip = this.sessionContext.getSessionGossip();
@@ -32320,7 +32489,8 @@ var init_dispatch_pipeline = __esm({
32320
32489
  consensusSummary: options?.consensus,
32321
32490
  specReviewContext,
32322
32491
  projectStructure: this.getProjectStructure(),
32323
- consensusFindings: consensusFindings.length > 0 ? consensusFindings : void 0
32492
+ consensusFindings: consensusFindings.length > 0 ? consensusFindings : void 0,
32493
+ agentCorrections: agentCorrections.length > 0 ? agentCorrections : void 0
32324
32494
  });
32325
32495
  this.taskGraph.recordCreated(taskId, agentId, task, agentSkills);
32326
32496
  const entry = {
@@ -40570,6 +40740,7 @@ __export(src_exports3, {
40570
40740
  JACCARD_THRESHOLD: () => JACCARD_THRESHOLD,
40571
40741
  KEY_REQUIRING_PROVIDERS: () => KEY_REQUIRING_PROVIDERS,
40572
40742
  LEDGER_INDEX_FILENAME: () => LEDGER_INDEX_FILENAME,
40743
+ LESSON_CARDS_MAX_PER_AGENT: () => LESSON_CARDS_MAX_PER_AGENT,
40573
40744
  LensGenerator: () => LensGenerator,
40574
40745
  MAX_ASSEMBLED_PROMPT_CHARS: () => MAX_ASSEMBLED_PROMPT_CHARS,
40575
40746
  MAX_CLAIMS_PER_BLOCK: () => MAX_CLAIMS_PER_BLOCK,
@@ -40613,6 +40784,7 @@ __export(src_exports3, {
40613
40784
  SkillEngine: () => SkillEngine,
40614
40785
  SkillGapTracker: () => SkillGapTracker,
40615
40786
  SkillIndex: () => SkillIndex,
40787
+ TERMINAL_LESSON_SIGNALS: () => TERMINAL_LESSON_SIGNALS,
40616
40788
  TIMEOUT_DAYS: () => TIMEOUT_DAYS,
40617
40789
  TIMEOUT_MS: () => TIMEOUT_MS,
40618
40790
  TOOL_SCHEMAS: () => TOOL_SCHEMAS2,
@@ -40780,6 +40952,7 @@ __export(src_exports3, {
40780
40952
  verifyClaims: () => verifyClaims,
40781
40953
  withResolverLock: () => withResolverLock,
40782
40954
  writeLedgerIndex: () => writeLedgerIndex,
40955
+ writeLessonCardsForSignals: () => writeLessonCardsForSignals,
40783
40956
  writeOrchestratorRoleMarker: () => writeOrchestratorRoleMarker
40784
40957
  });
40785
40958
  var init_src4 = __esm({
@@ -46503,11 +46676,11 @@ var init_routes = __esm({
46503
46676
  }
46504
46677
  if (!existsSync74(reportsDir)) return { reports: [], totalReports: 0, page, pageSize, retractedConsensusIds, roundRetractions };
46505
46678
  try {
46506
- const { statSync: statSync36 } = require("fs");
46679
+ const { statSync: statSync37 } = require("fs");
46507
46680
  const allFiles = readdirSync22(reportsDir).filter((f) => f.endsWith(".json")).sort((a, b) => {
46508
46681
  try {
46509
- const aTime = statSync36((0, import_path80.join)(reportsDir, a)).mtimeMs;
46510
- const bTime = statSync36((0, import_path80.join)(reportsDir, b)).mtimeMs;
46682
+ const aTime = statSync37((0, import_path80.join)(reportsDir, a)).mtimeMs;
46683
+ const bTime = statSync37((0, import_path80.join)(reportsDir, b)).mtimeMs;
46511
46684
  return bTime - aTime;
46512
46685
  } catch {
46513
46686
  return 0;
@@ -49375,7 +49548,7 @@ function appendRelayWarning(projectRoot, entry) {
49375
49548
  }
49376
49549
  function checkWorktreeEngaged(startedAt, projectRoot = process.cwd()) {
49377
49550
  try {
49378
- const { readdirSync: readdirSync22, statSync: statSync36 } = require("fs");
49551
+ const { readdirSync: readdirSync22, statSync: statSync37 } = require("fs");
49379
49552
  const { join: join93 } = require("path");
49380
49553
  const wtDir = join93(projectRoot, ".claude", "worktrees");
49381
49554
  let entries;
@@ -49388,7 +49561,7 @@ function checkWorktreeEngaged(startedAt, projectRoot = process.cwd()) {
49388
49561
  for (const entry of entries) {
49389
49562
  if (!entry.startsWith("agent-")) continue;
49390
49563
  try {
49391
- const st = statSync36(join93(wtDir, entry));
49564
+ const st = statSync37(join93(wtDir, entry));
49392
49565
  if (st.isDirectory() && st.mtimeMs >= startedAt - 2e3) return true;
49393
49566
  } catch {
49394
49567
  }
@@ -81731,7 +81904,7 @@ Ledger: ${ledgerPath2}`
81731
81904
  } catch {
81732
81905
  }
81733
81906
  try {
81734
- const { readdirSync: readdirSync22, statSync: statSync36, readFileSync: readFileSync70 } = await import("fs");
81907
+ const { readdirSync: readdirSync22, statSync: statSync37, readFileSync: readFileSync70 } = await import("fs");
81735
81908
  const { readJsonlWithRotated: readJsonlRotated1506 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
81736
81909
  const reportsDir = (0, import_path97.join)(process.cwd(), ".gossip", "consensus-reports");
81737
81910
  const perfPath = (0, import_path97.join)(process.cwd(), ".gossip", "agent-performance.jsonl");
@@ -81742,7 +81915,7 @@ Ledger: ${ledgerPath2}`
81742
81915
  for (const fname of readdirSync22(reportsDir)) {
81743
81916
  if (!fname.endsWith(".json")) continue;
81744
81917
  const fpath = (0, import_path97.join)(reportsDir, fname);
81745
- const st = statSync36(fpath);
81918
+ const st = statSync37(fpath);
81746
81919
  if (now - st.mtimeMs > WINDOW_MS3) continue;
81747
81920
  try {
81748
81921
  const report = JSON.parse(readFileSync70(fpath, "utf8"));
@@ -82567,6 +82740,7 @@ ${output}` }]
82567
82740
  agent_id: external_exports.string().describe("Agent being evaluated"),
82568
82741
  counterpart_id: external_exports.string().optional().describe("The other agent involved (e.g., who won the disagreement)"),
82569
82742
  finding: external_exports.string().describe("Brief description of the finding"),
82743
+ lesson: external_exports.string().optional().describe("Root-cause narrative for a terminal correction signal \u2014 1-2 sentences on why it failed and the check that would have caught it. Written verbatim into the discovering agent's lesson card (issue #642 A)."),
82570
82744
  finding_id: external_exports.string().optional().describe("Consensus finding ID \u2014 links this signal to a specific finding in a consensus report. Enables dashboard to resolve UNVERIFIED findings."),
82571
82745
  severity: external_exports.enum(["critical", "high", "medium", "low"]).optional().describe("Finding severity for impact scoring. If omitted, defaults to medium."),
82572
82746
  category: external_exports.string().optional().describe("Finding category for ATI competency profiles (e.g., concurrency, trust_boundaries, injection_vectors, resource_exhaustion, type_safety, error_handling, data_integrity, input_validation)"),
@@ -83055,6 +83229,11 @@ Uncategorized finding_ids: ${uncategorized.join(", ")}`;
83055
83229
  if (dedupedConsensus.length > 0) emitRecordConsensusSignals(process.cwd(), dedupedConsensus);
83056
83230
  if (dedupedImpl.length > 0) emitRecordImplSignals(process.cwd(), dedupedImpl);
83057
83231
  }
83232
+ try {
83233
+ const { writeLessonCardsForSignals: writeLessonCardsForSignals2 } = await Promise.resolve().then(() => (init_src4(), src_exports3));
83234
+ writeLessonCardsForSignals2(process.cwd(), signals ?? []);
83235
+ } catch {
83236
+ }
83058
83237
  const hallucinationSignals = deduped.filter(
83059
83238
  (s) => s.type === "consensus" && s.signal === "hallucination_caught"
83060
83239
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gossipcat",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "Multi-agent orchestration for Claude Code — parallel review, consensus, adaptive dispatch",
5
5
  "mcpName": "io.github.ataberk-xyz/gossipcat",
6
6
  "repository": {
@@ -34,7 +34,7 @@
34
34
  "test": "jest --config jest.config.base.js",
35
35
  "clean": "rm -rf packages/*/dist apps/*/dist dist-mcp/ dist-dashboard/",
36
36
  "postinstall": "node scripts/postinstall.js",
37
- "build:mcp": "rm -rf dist-mcp && npx esbuild apps/cli/src/mcp-server-sdk.ts --bundle --platform=node --target=node22 --outfile=dist-mcp/mcp-server.js --external:bufferutil --external:utf-8-validate --tsconfig=tsconfig.json && cp -r packages/orchestrator/src/default-skills dist-mcp/default-skills && cp -r packages/orchestrator/src/default-rules dist-mcp/default-rules && cp -r assets dist-mcp/assets && mkdir -p dist-mcp/data && cp data/archetypes.json dist-mcp/data/archetypes.json && chmod +x dist-mcp/mcp-server.js && chmod +x dist-mcp/assets/hooks/worktree-sandbox.sh && chmod +x dist-mcp/assets/hooks/discipline/*.sh",
37
+ "build:mcp": "rm -rf dist-mcp && npx esbuild apps/cli/src/mcp-server-sdk.ts --bundle --platform=node --target=node22 --outfile=dist-mcp/mcp-server.js --external:bufferutil --external:utf-8-validate --external:re2 --tsconfig=tsconfig.json && cp -r packages/orchestrator/src/default-skills dist-mcp/default-skills && cp -r packages/orchestrator/src/default-rules dist-mcp/default-rules && cp -r assets dist-mcp/assets && mkdir -p dist-mcp/data && cp data/archetypes.json dist-mcp/data/archetypes.json && chmod +x dist-mcp/mcp-server.js && chmod +x dist-mcp/assets/hooks/worktree-sandbox.sh && chmod +x dist-mcp/assets/hooks/discipline/*.sh",
38
38
  "build:dashboard": "cd packages/dashboard-v2 && npx vite build --outDir ../../dist-dashboard --emptyOutDir",
39
39
  "prepublishOnly": "npm run build:all && npm run build:mcp && npm run build:dashboard",
40
40
  "gossipcat": "npx ts-node -r tsconfig-paths/register -P tsconfig.json apps/cli/src/index.ts",
@@ -55,6 +55,7 @@
55
55
  "dependencies": {
56
56
  "@clack/prompts": "^1.1.0",
57
57
  "@modelcontextprotocol/sdk": "^1.27.1",
58
+ "re2": "^1.25.0",
58
59
  "tsconfig-paths": "^4.2.0",
59
60
  "ws": "^8.20.1"
60
61
  },