let-them-talk 5.5.2 → 5.5.3

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 CHANGED
@@ -310,7 +310,7 @@ The verify suite doesn't claim to cover every provider or runtime matrix, and do
310
310
  - **Rate-limited** API endpoints on non-localhost requests.
311
311
  - **No telemetry, no cloud.** Everything runs locally.
312
312
  - **Obsidian-quality rich rendering** — GFM tables, fenced code with syntax highlighting (highlight.js), Obsidian-style callouts (`> [!NOTE]`, `> [!WARNING]`, `> [!SUMMARY]-` collapsible), Mermaid diagrams, KaTeX math, clickable image lightbox, copy-code buttons. Every shipping lib is bundled locally under `vendor/` so the dashboard works offline.
313
- - **0 known vulnerabilities** in the shipped tarball as of v5.5.2.
313
+ - **0 known vulnerabilities** in the shipped tarball as of v5.5.3.
314
314
  - **Sensitive-path blocks** on file-share: `.env`, `.pem`, `.key`, `.lan-token`, `mcp.json`, and the agent-bridge data directory cannot be shared.
315
315
  - See [`SECURITY.md`](SECURITY.md) for the disclosure policy.
316
316
 
package/USAGE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <!-- Generated from ../USAGE.md by scripts/sync-packaged-docs.js for published package consumers. -->
2
2
 
3
- # Let Them Talk Usage Guide v5.5.2
3
+ # Let Them Talk Usage Guide v5.5.3
4
4
 
5
5
  This guide is the short operator view of the current runtime. For normative architecture details, use the docs under `docs/architecture/`.
6
6
 
package/cli.js CHANGED
@@ -9,7 +9,7 @@ const { createCanonicalState } = require('./state/canonical');
9
9
 
10
10
  function printUsage() {
11
11
  console.log(`
12
- Let Them Talk — Agent Bridge v5.5.2
12
+ Let Them Talk — Agent Bridge v5.5.3
13
13
  MCP message broker for inter-agent communication
14
14
  Supports: Claude Code, Gemini CLI, Codex CLI, Ollama
15
15
 
@@ -30,6 +30,8 @@ function printUsage() {
30
30
  node .agent-bridge/launch.js reset Clear all conversation data
31
31
  node .agent-bridge/launch.js migrate Backfill canonical event stream from legacy projections
32
32
  node .agent-bridge/launch.js migrate --dry-run Preview what migrate would do
33
+ node .agent-bridge/launch.js repair Repair corrupted canonical event log (drops orphan redactions)
34
+ node .agent-bridge/launch.js repair --dry-run Preview what repair would do
33
35
 
34
36
  Or via npx (re-downloads each time):
35
37
  npx let-them-talk dashboard
@@ -879,6 +881,15 @@ function cliStatus() {
879
881
  console.log('');
880
882
  }
881
883
 
884
+ function cliRepairEvents() {
885
+ const { repairBranch: _ } = require('./scripts/repair-canonical-events');
886
+ // Delegate to the script's main — it reads process.argv and handles --dry-run
887
+ const scriptPath = path.join(__dirname, 'scripts', 'repair-canonical-events.js');
888
+ const args = process.argv.slice(3);
889
+ const child = require('child_process').spawnSync(process.execPath, [scriptPath, ...args], { stdio: 'inherit' });
890
+ if (child.status !== 0) process.exit(child.status || 1);
891
+ }
892
+
882
893
  function cliMigrate() {
883
894
  const args = process.argv.slice(3);
884
895
  const dryRun = args.includes('--dry-run') || args.includes('-n');
@@ -1143,6 +1154,10 @@ function runCli() {
1143
1154
  case 'migrate-legacy':
1144
1155
  cliMigrate();
1145
1156
  break;
1157
+ case 'repair':
1158
+ case 'repair-events':
1159
+ cliRepairEvents();
1160
+ break;
1146
1161
  case 'msg':
1147
1162
  case 'message':
1148
1163
  case 'send':
package/dashboard.js CHANGED
@@ -3445,7 +3445,7 @@ server.listen(PORT, LAN_MODE ? '0.0.0.0' : '127.0.0.1', () => {
3445
3445
  const dataDir = resolveDataDir();
3446
3446
  const lanIP = getLanIP();
3447
3447
  console.log('');
3448
- console.log(' Let Them Talk - Agent Bridge Dashboard v5.5.2');
3448
+ console.log(' Let Them Talk - Agent Bridge Dashboard v5.5.3');
3449
3449
  console.log(' ============================================');
3450
3450
  console.log(' Dashboard: http://localhost:' + PORT);
3451
3451
  if (LAN_MODE && lanIP) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "let-them-talk",
3
- "version": "5.5.2",
3
+ "version": "5.5.3",
4
4
  "description": "MCP message broker + web dashboard for inter-agent communication. Let AI CLI agents talk to each other.",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env node
2
+ // Repair a corrupted canonical event log in-place.
3
+ //
4
+ // Problem this fixes:
5
+ // Pre-v5.5.3 Clear Messages would emit a message.redacted event for every
6
+ // message currently in the projection, even if that message had no
7
+ // corresponding message.sent event in the canonical log (e.g. legacy
8
+ // projection-only messages left over from a partial migration, or an
9
+ // earlier clear cycle). On the next rebuild/replay the redacted event
10
+ // fails with "cannot apply message.redacted because message X does not
11
+ // exist", which blocks further Clear Messages on that branch.
12
+ //
13
+ // v5.5.3 fixed the root cause in clearMessages so new redactions are
14
+ // gated on the presence of a message.sent ancestor. This script cleans
15
+ // up the orphan redactions that are already in the event log from
16
+ // previous versions so the branch can replay again.
17
+ //
18
+ // What it does:
19
+ // - For every branch under .agent-bridge/runtime/branches/<branch>/:
20
+ // - Reads events.jsonl
21
+ // - Collects the set of message IDs that have a message.sent event
22
+ // - Drops message.redacted and message.corrected events whose
23
+ // payload.message_id is not in that set (orphans)
24
+ // - Backs up the original events.jsonl to events.jsonl.pre-repair-<ts>
25
+ // - Writes the cleaned stream back
26
+ // - Deletes the branch's projection files (messages.jsonl, history.jsonl,
27
+ // dashboard-query-projection.json, events.head.json) so the runtime
28
+ // rebuilds them cleanly on the next read.
29
+ //
30
+ // Usage:
31
+ // node agent-bridge/scripts/repair-canonical-events.js [project-path]
32
+ // node agent-bridge/scripts/repair-canonical-events.js --dry-run [project-path]
33
+ //
34
+ // project-path defaults to the current working directory.
35
+
36
+ const fs = require('fs');
37
+ const path = require('path');
38
+ const { resolveDataDir } = require('../data-dir');
39
+
40
+ function readJsonl(file) {
41
+ if (!fs.existsSync(file)) return [];
42
+ return fs
43
+ .readFileSync(file, 'utf8')
44
+ .split(/\r?\n/)
45
+ .filter((line) => line.trim())
46
+ .map((line) => {
47
+ try { return JSON.parse(line); } catch { return null; }
48
+ })
49
+ .filter(Boolean);
50
+ }
51
+
52
+ function repairBranch(branchDir, opts) {
53
+ const eventsFile = path.join(branchDir, 'events.jsonl');
54
+ if (!fs.existsSync(eventsFile)) return { skipped: true, reason: 'no events.jsonl' };
55
+
56
+ const events = readJsonl(eventsFile);
57
+ if (events.length === 0) return { skipped: true, reason: 'empty events.jsonl' };
58
+
59
+ const sentIds = new Set();
60
+ for (const ev of events) {
61
+ if (ev && ev.type === 'message.sent' && ev.payload && ev.payload.message && typeof ev.payload.message.id === 'string') {
62
+ sentIds.add(ev.payload.message.id);
63
+ }
64
+ }
65
+
66
+ const kept = [];
67
+ const orphans = [];
68
+ for (const ev of events) {
69
+ if (ev && (ev.type === 'message.redacted' || ev.type === 'message.corrected')) {
70
+ const msgId = ev.payload && ev.payload.message_id;
71
+ if (msgId && !sentIds.has(msgId)) {
72
+ orphans.push(ev);
73
+ continue;
74
+ }
75
+ }
76
+ kept.push(ev);
77
+ }
78
+
79
+ const result = {
80
+ branch: path.basename(branchDir),
81
+ total_events: events.length,
82
+ message_sent_events: sentIds.size,
83
+ orphan_redacted: orphans.filter((o) => o.type === 'message.redacted').length,
84
+ orphan_corrected: orphans.filter((o) => o.type === 'message.corrected').length,
85
+ kept_events: kept.length,
86
+ };
87
+
88
+ if (opts.dryRun) {
89
+ result.dryRun = true;
90
+ return result;
91
+ }
92
+
93
+ if (orphans.length === 0) {
94
+ result.skipped = true;
95
+ result.reason = 'no orphan events';
96
+ return result;
97
+ }
98
+
99
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
100
+ const backupFile = path.join(branchDir, `events.jsonl.pre-repair-${stamp}`);
101
+ fs.copyFileSync(eventsFile, backupFile);
102
+ result.backup = backupFile;
103
+
104
+ fs.writeFileSync(eventsFile, kept.map((e) => JSON.stringify(e)).join('\n') + '\n');
105
+
106
+ // Drop cached projections + head pointer so the runtime rebuilds next read
107
+ for (const name of ['messages.jsonl', 'history.jsonl', 'dashboard-query-projection.json', 'events.head.json']) {
108
+ const p = path.join(branchDir, name);
109
+ if (fs.existsSync(p)) fs.unlinkSync(p);
110
+ }
111
+ result.projections_cleared = true;
112
+
113
+ return result;
114
+ }
115
+
116
+ function main(argv) {
117
+ const args = argv.slice(2);
118
+ const dryRun = args.includes('--dry-run') || args.includes('-n');
119
+ const projectArg = args.filter((a) => !a.startsWith('-'))[0] || process.cwd();
120
+
121
+ const dataDir = resolveDataDir({ cwd: projectArg });
122
+ const branchesDir = path.join(dataDir, 'runtime', 'branches');
123
+
124
+ console.log('');
125
+ console.log(' Canonical event log repair');
126
+ console.log(' ==========================');
127
+ console.log(' Project: ' + projectArg);
128
+ console.log(' Runtime: ' + branchesDir);
129
+ console.log(dryRun ? ' Mode: DRY RUN (no changes written)' : ' Mode: apply');
130
+ console.log('');
131
+
132
+ if (!fs.existsSync(branchesDir)) {
133
+ console.log(' [info] No canonical runtime at this path. Nothing to repair.');
134
+ return;
135
+ }
136
+
137
+ const branches = fs.readdirSync(branchesDir).filter((name) => {
138
+ const p = path.join(branchesDir, name);
139
+ return fs.statSync(p).isDirectory();
140
+ });
141
+
142
+ if (branches.length === 0) {
143
+ console.log(' [info] No branches present. Nothing to repair.');
144
+ return;
145
+ }
146
+
147
+ for (const name of branches) {
148
+ const r = repairBranch(path.join(branchesDir, name), { dryRun });
149
+ console.log(` Branch "${name}":`);
150
+ if (r.skipped) {
151
+ console.log(' skipped — ' + r.reason);
152
+ } else {
153
+ console.log(` ${r.total_events} total events, ${r.message_sent_events} sent messages`);
154
+ console.log(` orphan redactions: ${r.orphan_redacted}, orphan corrections: ${r.orphan_corrected}`);
155
+ if (!dryRun) {
156
+ console.log(' [ok] rewrote events.jsonl (' + r.kept_events + ' events kept)');
157
+ console.log(' [ok] backup: ' + r.backup);
158
+ console.log(' [ok] projections cleared — will rebuild on next read');
159
+ } else {
160
+ console.log(' [dry-run] would drop ' + (r.orphan_redacted + r.orphan_corrected) + ' orphan event(s) and back up the original');
161
+ }
162
+ }
163
+ }
164
+
165
+ console.log('');
166
+ if (!dryRun) {
167
+ console.log(' Done. Clear Messages should now work on the repaired branches.');
168
+ } else {
169
+ console.log(' Re-run without --dry-run to apply.');
170
+ }
171
+ console.log('');
172
+ }
173
+
174
+ if (require.main === module) {
175
+ try { main(process.argv); }
176
+ catch (e) { console.error(' [error] ' + (e && e.stack ? e.stack : e)); process.exit(1); }
177
+ }
178
+
179
+ module.exports = { repairBranch };
package/server.js CHANGED
@@ -3708,7 +3708,7 @@ function buildListenGroupResponse(batch, consumed, agentName, listenStart) {
3708
3708
  return new Date(a.timestamp) - new Date(b.timestamp);
3709
3709
  });
3710
3710
 
3711
- // LEAN RESPONSE (v5.5.2+): the agent already has every prior message in its
3711
+ // LEAN RESPONSE (v5.5.3+): the agent already has every prior message in its
3712
3712
  // own LLM context, plus the full rule set from get_guide() + AGENTS.md. We
3713
3713
  // only send the NEW messages + the managed-mode signals needed for
3714
3714
  // turn-taking. No repeated reminders, no agent rosters, no "next_action"
@@ -8136,7 +8136,7 @@ function toolToggleRule(ruleId) {
8136
8136
  // --- MCP Server setup ---
8137
8137
 
8138
8138
  const server = new Server(
8139
- { name: 'agent-bridge', version: '5.5.2' },
8139
+ { name: 'agent-bridge', version: '5.5.3' },
8140
8140
  { capabilities: { tools: {} } }
8141
8141
  );
8142
8142
 
@@ -9265,7 +9265,7 @@ async function main() {
9265
9265
  try {
9266
9266
  const transport = new StdioServerTransport();
9267
9267
  await server.connect(transport);
9268
- console.error('Agent Bridge MCP server v5.5.2 running (65 tools)');
9268
+ console.error('Agent Bridge MCP server v5.5.3 running (65 tools)');
9269
9269
  } catch (e) {
9270
9270
  console.error('ERROR: MCP server failed to start: ' + e.message);
9271
9271
  console.error('Fix: Run "npx let-them-talk doctor" to check your setup.');
@@ -2770,8 +2770,22 @@ function createCanonicalState(options = {}) {
2770
2770
  const redactedAt = params.redactedAt || new Date().toISOString();
2771
2771
  const clearedMessageIds = [];
2772
2772
 
2773
+ // Only redact messages that actually have a canonical message.sent event
2774
+ // in this branch's log. Redacting a message that was never "sent" in
2775
+ // canonical terms (e.g. a legacy projection-only message left over from
2776
+ // pre-canonical clear cycles) would create an orphan redaction that
2777
+ // breaks future replay — the rebuild fails with "cannot apply
2778
+ // message.redacted because message X does not exist".
2779
+ const canonicalSentIds = new Set();
2780
+ for (const event of readCanonicalMessageEvents(branch)) {
2781
+ if (event && event.type === 'message.sent' && event.payload && event.payload.message && typeof event.payload.message.id === 'string') {
2782
+ canonicalSentIds.add(event.payload.message.id);
2783
+ }
2784
+ }
2785
+
2773
2786
  for (const message of Array.isArray(currentMessages) ? currentMessages : []) {
2774
2787
  if (!message || typeof message.id !== 'string' || !message.id) continue;
2788
+ if (!canonicalSentIds.has(message.id)) continue; // skip projection-only / orphan
2775
2789
  appendCanonicalMessageRedactedEvent({
2776
2790
  branch,
2777
2791
  actorAgent,