let-them-talk 5.4.3 → 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 +2 -1
- package/USAGE.md +1 -1
- package/cli.js +27 -3
- package/conversation-templates/autonomous-feature.json +4 -4
- package/conversation-templates/code-review.json +3 -3
- package/conversation-templates/debug-squad.json +3 -3
- package/conversation-templates/feature-build.json +3 -3
- package/conversation-templates/research-write.json +3 -3
- package/dashboard.html +329 -158
- package/dashboard.js +3459 -3429
- package/package.json +114 -113
- package/scripts/repair-canonical-events.js +179 -0
- package/server.js +26 -85
- package/state/canonical.js +14 -0
- package/templates/debate.json +2 -2
- package/templates/managed.json +4 -4
- package/templates/pair.json +2 -2
- package/templates/review.json +2 -2
- package/templates/team.json +3 -3
- package/vendor/highlight-github-dark.min.css +10 -0
- package/vendor/highlight.min.js +1232 -0
- package/vendor/katex-fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/vendor/katex-fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/vendor/katex.min.css +1 -0
- package/vendor/katex.min.js +1 -0
- package/vendor/marked.min.js +6 -0
- package/vendor/mermaid.min.js +2314 -0
package/package.json
CHANGED
|
@@ -1,113 +1,114 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "let-them-talk",
|
|
3
|
-
"version": "5.
|
|
4
|
-
"description": "MCP message broker + web dashboard for inter-agent communication. Let AI CLI agents talk to each other.",
|
|
5
|
-
"main": "server.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"agent-bridge": "./cli.js",
|
|
8
|
-
"let-them-talk": "./cli.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"start": "node server.js",
|
|
12
|
-
"dashboard": "node dashboard.js",
|
|
13
|
-
"export:markdown-workspace": "node scripts/export-markdown-workspace.js",
|
|
14
|
-
"sync:packaged-docs": "node scripts/sync-packaged-docs.js",
|
|
15
|
-
"prepack": "npm run sync:packaged-docs",
|
|
16
|
-
"test": "npm run verify",
|
|
17
|
-
"verify": "npm run verify:contracts && npm run verify:replay && npm run verify:invariants",
|
|
18
|
-
"verify:docs-onboarding": "node scripts/check-docs-onboarding.js",
|
|
19
|
-
"verify:contracts": "npm run verify:contracts:runtime && npm run verify:contracts:schema && npm run verify:contracts:branches && npm run verify:contracts:markdown-workspace",
|
|
20
|
-
"verify:contracts:runtime": "node scripts/check-runtime-contract.js",
|
|
21
|
-
"verify:contracts:schema": "node scripts/check-event-schema.js",
|
|
22
|
-
"verify:contracts:branches": "node scripts/check-branch-semantics.js",
|
|
23
|
-
"verify:contracts:markdown-workspace": "node scripts/check-markdown-workspace.js",
|
|
24
|
-
"verify:replay": "npm run verify:replay:positive && npm run verify:replay:negative",
|
|
25
|
-
"verify:replay:positive": "npm run verify:replay:healthy && npm run verify:replay:clean",
|
|
26
|
-
"verify:replay:healthy": "node scripts/check-message-replay.js --scenario healthy",
|
|
27
|
-
"verify:replay:clean": "node scripts/check-message-replay.js --scenario clean",
|
|
28
|
-
"verify:replay:negative": "node scripts/run-verification-suite.js replay-negative",
|
|
29
|
-
"verify:invariants": "npm run verify:invariants:authority && npm run verify:invariants:dashboard-control-plane && npm run verify:invariants:performance-indexing && npm run verify:invariants:capabilities && npm run verify:invariants:api-agent-parity && npm run verify:invariants:dashboard-semantic-gap && npm run verify:invariants:migration-hardening && npm run verify:invariants:branches && npm run verify:invariants:sessions && npm run verify:invariants:evidence && npm run verify:invariants:context && npm run verify:invariants:autonomy-v2 && npm run verify:invariants:autonomy-v2-watchdog && npm run verify:invariants:autonomy-v2-execution && npm run verify:invariants:agent-contracts && npm run verify:invariants:managed-team-integration && npm run verify:invariants:lifecycle-hooks && npm run verify:invariants:markdown-workspace-export && npm run verify:invariants:markdown-workspace-safety",
|
|
30
|
-
"verify:invariants:authority": "node scripts/check-invariants.js --suite authority",
|
|
31
|
-
"verify:invariants:dashboard-control-plane": "node scripts/check-dashboard-control-plane.js",
|
|
32
|
-
"verify:invariants:performance-indexing": "node scripts/check-performance-indexing.js",
|
|
33
|
-
"verify:invariants:capabilities": "node scripts/check-provider-capabilities.js",
|
|
34
|
-
"verify:invariants:api-agent-parity": "node scripts/check-api-agent-parity.js",
|
|
35
|
-
"verify:invariants:dashboard-semantic-gap": "node scripts/run-verification-suite.js dashboard-semantic-gap",
|
|
36
|
-
"verify:invariants:migration-hardening": "node scripts/check-migration-hardening.js",
|
|
37
|
-
"verify:invariants:branches": "node scripts/check-branch-isolation.js && node scripts/check-branch-fork-snapshot.js",
|
|
38
|
-
"verify:invariants:branch-fork": "node scripts/check-branch-fork-snapshot.js",
|
|
39
|
-
"verify:invariants:sessions": "node scripts/check-session-lifecycle.js",
|
|
40
|
-
"verify:invariants:evidence": "node scripts/check-evidence-completion.js",
|
|
41
|
-
"verify:invariants:context": "node scripts/check-session-aware-context.js",
|
|
42
|
-
"verify:invariants:autonomy-v2": "node scripts/check-autonomy-v2-decision.js",
|
|
43
|
-
"verify:invariants:autonomy-v2-watchdog": "node scripts/check-autonomy-v2-watchdog.js",
|
|
44
|
-
"verify:invariants:autonomy-v2-execution": "node scripts/check-autonomy-v2-execution.js",
|
|
45
|
-
"verify:invariants:agent-contracts": "node scripts/check-agent-contract-advisory.js",
|
|
46
|
-
"verify:invariants:managed-team-integration": "node scripts/check-managed-team-integration.js",
|
|
47
|
-
"verify:invariants:lifecycle-hooks": "node scripts/check-lifecycle-hooks.js",
|
|
48
|
-
"verify:invariants:markdown-workspace-export": "node scripts/check-markdown-workspace-export.js",
|
|
49
|
-
"verify:invariants:markdown-workspace-safety": "node scripts/check-markdown-workspace-safety.js",
|
|
50
|
-
"verify:smoke": "node scripts/run-verification-suite.js smoke"
|
|
51
|
-
},
|
|
52
|
-
"engines": {
|
|
53
|
-
"node": ">=18.0.0"
|
|
54
|
-
},
|
|
55
|
-
"files": [
|
|
56
|
-
"data-dir.js",
|
|
57
|
-
"server.js",
|
|
58
|
-
"dashboard.js",
|
|
59
|
-
"dashboard.html",
|
|
60
|
-
"api-agents.js",
|
|
61
|
-
"runtime-descriptor.js",
|
|
62
|
-
"agent-contracts.js",
|
|
63
|
-
"managed-team-integration.js",
|
|
64
|
-
"autonomy/",
|
|
65
|
-
"events/",
|
|
66
|
-
"state/",
|
|
67
|
-
"providers/",
|
|
68
|
-
"office/",
|
|
69
|
-
"mods/",
|
|
70
|
-
"scripts/",
|
|
71
|
-
"docs/",
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"
|
|
84
|
-
"claude
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
|
|
113
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "let-them-talk",
|
|
3
|
+
"version": "5.5.3",
|
|
4
|
+
"description": "MCP message broker + web dashboard for inter-agent communication. Let AI CLI agents talk to each other.",
|
|
5
|
+
"main": "server.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agent-bridge": "./cli.js",
|
|
8
|
+
"let-them-talk": "./cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node server.js",
|
|
12
|
+
"dashboard": "node dashboard.js",
|
|
13
|
+
"export:markdown-workspace": "node scripts/export-markdown-workspace.js",
|
|
14
|
+
"sync:packaged-docs": "node scripts/sync-packaged-docs.js",
|
|
15
|
+
"prepack": "npm run sync:packaged-docs",
|
|
16
|
+
"test": "npm run verify",
|
|
17
|
+
"verify": "npm run verify:contracts && npm run verify:replay && npm run verify:invariants",
|
|
18
|
+
"verify:docs-onboarding": "node scripts/check-docs-onboarding.js",
|
|
19
|
+
"verify:contracts": "npm run verify:contracts:runtime && npm run verify:contracts:schema && npm run verify:contracts:branches && npm run verify:contracts:markdown-workspace",
|
|
20
|
+
"verify:contracts:runtime": "node scripts/check-runtime-contract.js",
|
|
21
|
+
"verify:contracts:schema": "node scripts/check-event-schema.js",
|
|
22
|
+
"verify:contracts:branches": "node scripts/check-branch-semantics.js",
|
|
23
|
+
"verify:contracts:markdown-workspace": "node scripts/check-markdown-workspace.js",
|
|
24
|
+
"verify:replay": "npm run verify:replay:positive && npm run verify:replay:negative",
|
|
25
|
+
"verify:replay:positive": "npm run verify:replay:healthy && npm run verify:replay:clean",
|
|
26
|
+
"verify:replay:healthy": "node scripts/check-message-replay.js --scenario healthy",
|
|
27
|
+
"verify:replay:clean": "node scripts/check-message-replay.js --scenario clean",
|
|
28
|
+
"verify:replay:negative": "node scripts/run-verification-suite.js replay-negative",
|
|
29
|
+
"verify:invariants": "npm run verify:invariants:authority && npm run verify:invariants:dashboard-control-plane && npm run verify:invariants:performance-indexing && npm run verify:invariants:capabilities && npm run verify:invariants:api-agent-parity && npm run verify:invariants:dashboard-semantic-gap && npm run verify:invariants:migration-hardening && npm run verify:invariants:branches && npm run verify:invariants:sessions && npm run verify:invariants:evidence && npm run verify:invariants:context && npm run verify:invariants:autonomy-v2 && npm run verify:invariants:autonomy-v2-watchdog && npm run verify:invariants:autonomy-v2-execution && npm run verify:invariants:agent-contracts && npm run verify:invariants:managed-team-integration && npm run verify:invariants:lifecycle-hooks && npm run verify:invariants:markdown-workspace-export && npm run verify:invariants:markdown-workspace-safety",
|
|
30
|
+
"verify:invariants:authority": "node scripts/check-invariants.js --suite authority",
|
|
31
|
+
"verify:invariants:dashboard-control-plane": "node scripts/check-dashboard-control-plane.js",
|
|
32
|
+
"verify:invariants:performance-indexing": "node scripts/check-performance-indexing.js",
|
|
33
|
+
"verify:invariants:capabilities": "node scripts/check-provider-capabilities.js",
|
|
34
|
+
"verify:invariants:api-agent-parity": "node scripts/check-api-agent-parity.js",
|
|
35
|
+
"verify:invariants:dashboard-semantic-gap": "node scripts/run-verification-suite.js dashboard-semantic-gap",
|
|
36
|
+
"verify:invariants:migration-hardening": "node scripts/check-migration-hardening.js",
|
|
37
|
+
"verify:invariants:branches": "node scripts/check-branch-isolation.js && node scripts/check-branch-fork-snapshot.js",
|
|
38
|
+
"verify:invariants:branch-fork": "node scripts/check-branch-fork-snapshot.js",
|
|
39
|
+
"verify:invariants:sessions": "node scripts/check-session-lifecycle.js",
|
|
40
|
+
"verify:invariants:evidence": "node scripts/check-evidence-completion.js",
|
|
41
|
+
"verify:invariants:context": "node scripts/check-session-aware-context.js",
|
|
42
|
+
"verify:invariants:autonomy-v2": "node scripts/check-autonomy-v2-decision.js",
|
|
43
|
+
"verify:invariants:autonomy-v2-watchdog": "node scripts/check-autonomy-v2-watchdog.js",
|
|
44
|
+
"verify:invariants:autonomy-v2-execution": "node scripts/check-autonomy-v2-execution.js",
|
|
45
|
+
"verify:invariants:agent-contracts": "node scripts/check-agent-contract-advisory.js",
|
|
46
|
+
"verify:invariants:managed-team-integration": "node scripts/check-managed-team-integration.js",
|
|
47
|
+
"verify:invariants:lifecycle-hooks": "node scripts/check-lifecycle-hooks.js",
|
|
48
|
+
"verify:invariants:markdown-workspace-export": "node scripts/check-markdown-workspace-export.js",
|
|
49
|
+
"verify:invariants:markdown-workspace-safety": "node scripts/check-markdown-workspace-safety.js",
|
|
50
|
+
"verify:smoke": "node scripts/run-verification-suite.js smoke"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
},
|
|
55
|
+
"files": [
|
|
56
|
+
"data-dir.js",
|
|
57
|
+
"server.js",
|
|
58
|
+
"dashboard.js",
|
|
59
|
+
"dashboard.html",
|
|
60
|
+
"api-agents.js",
|
|
61
|
+
"runtime-descriptor.js",
|
|
62
|
+
"agent-contracts.js",
|
|
63
|
+
"managed-team-integration.js",
|
|
64
|
+
"autonomy/",
|
|
65
|
+
"events/",
|
|
66
|
+
"state/",
|
|
67
|
+
"providers/",
|
|
68
|
+
"office/",
|
|
69
|
+
"mods/",
|
|
70
|
+
"scripts/",
|
|
71
|
+
"docs/",
|
|
72
|
+
"vendor/",
|
|
73
|
+
"USAGE.md",
|
|
74
|
+
"cli.js",
|
|
75
|
+
"templates/",
|
|
76
|
+
"conversation-templates/",
|
|
77
|
+
"logo.png",
|
|
78
|
+
"LICENSE",
|
|
79
|
+
"SECURITY.md",
|
|
80
|
+
"CHANGELOG.md"
|
|
81
|
+
],
|
|
82
|
+
"keywords": [
|
|
83
|
+
"mcp",
|
|
84
|
+
"claude",
|
|
85
|
+
"claude-code",
|
|
86
|
+
"gemini-cli",
|
|
87
|
+
"codex-cli",
|
|
88
|
+
"agent",
|
|
89
|
+
"multi-agent",
|
|
90
|
+
"communication",
|
|
91
|
+
"message-broker",
|
|
92
|
+
"ai-agents",
|
|
93
|
+
"let-them-talk"
|
|
94
|
+
],
|
|
95
|
+
"repository": {
|
|
96
|
+
"type": "git",
|
|
97
|
+
"url": "git+https://github.com/Dekelelz/let-them-talk.git"
|
|
98
|
+
},
|
|
99
|
+
"homepage": "https://talk.unrealai.studio",
|
|
100
|
+
"bugs": {
|
|
101
|
+
"url": "https://github.com/Dekelelz/let-them-talk/issues"
|
|
102
|
+
},
|
|
103
|
+
"author": "Dekelelz <contact@talk.unrealai.studio>",
|
|
104
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
105
|
+
"dependencies": {
|
|
106
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
107
|
+
"three": "0.175.0"
|
|
108
|
+
},
|
|
109
|
+
"overrides": {
|
|
110
|
+
"hono": "^4.12.14",
|
|
111
|
+
"path-to-regexp": "^8.4.2",
|
|
112
|
+
"@hono/node-server": "^1.19.14"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -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
|
@@ -1918,6 +1918,8 @@ function buildGuide(level = 'standard') {
|
|
|
1918
1918
|
rules.push('SELF-RELIANCE RULE: When the Owner gives you a goal, treat it as a goal — NOT a checklist of approval gates. Break it down yourself, pick tasks via get_work(), and work until done. NEVER stop to ask "should I do X?" or "do you want me to Y?" for decisions you and the team can make. Your default answer to uncertainty is: decide, log_decision() to record the choice, continue. Asking the Owner for permission on small decisions is the failure mode — deciding and moving is the success mode.');
|
|
1919
1919
|
rules.push('TEAM-FIRST ESCALATION RULE: Before DMing Dashboard/Owner with a question, try these in order: (1) kb_read() — did the team already decide this? (2) DM a teammate with the relevant skill (use list_agents() to find them). (3) call_vote() if the team genuinely disagrees. (4) log_decision() to lock in your choice and move forward. Only escalate to Owner when: (a) the overall goal is complete and the next strategic direction genuinely needs a human call, or (b) you hit a true blocker only the Owner can resolve (credentials, priorities, business rules, access). "I am not sure which design to pick" is NOT an Owner question — it is a team_decision() question.');
|
|
1920
1920
|
rules.push('DONE-WHEN-DONE RULE: "Done" means the Owner\'s original GOAL is achieved, not "I finished my current step". After verify_and_advance(), immediately call get_work() again to find the next piece of the goal. The loop ends when the goal is complete and evidence is recorded — not when the current step ends. If get_work() returns nothing and the goal still is not done, synthesize: break the remaining work into new tasks with create_task() and keep going.');
|
|
1921
|
+
rules.push('FORMATTING RULE (dashboard is rendered rich markdown): The Messages tab renders GFM markdown, GitHub-quality tables, fenced code with syntax highlighting, Obsidian-style callouts, Mermaid diagrams, KaTeX math, and clickable images. WRITE LIKE YOU ARE PUBLISHING. Use: (1) **tables** for structured data (status, file changes, comparisons) — NEVER use indented lists for tabular info; (2) fenced code blocks with language tag (```ts, ```bash, ```json); (3) callouts for status: > [!SUCCESS] when something shipped, > [!WARNING] for risks, > [!DANGER] for blockers, > [!NOTE] for context, > [!SUMMARY]- (collapsible) for long reports; (4) ```mermaid blocks for architecture/flow/sequence diagrams instead of ASCII art; (5) headings (##, ###) to structure long updates; (6) task lists (- [x] done / - [ ] todo) for action items. A terse structured report beats a wall of text.');
|
|
1922
|
+
rules.push('STATUS-REPORT TEMPLATE: When reporting progress to the Owner or Quality Lead, follow this shape:\n\n> [!SUMMARY]- Headline (1 sentence)\n> \n> ## What shipped\n> | Area | Change | Evidence |\n> |---|---|---|\n> | ... | ... | files_changed + verification |\n> \n> ## Blockers\n> > [!WARNING] describe blocker + what unblocks it\n> \n> ## Next\n> - [ ] next step 1\n> - [ ] next step 2\n\nIf nothing is blocked, omit the Blockers section. If you are only reporting a small update, skip the collapsible summary and use a callout with the headline inline. Do not narrate in prose when a table would be clearer.');
|
|
1921
1923
|
|
|
1922
1924
|
// Minimal level: Tier 0 only — for experienced agents refreshing rules
|
|
1923
1925
|
if (level === 'minimal') {
|
|
@@ -3607,14 +3609,13 @@ async function toolListenGroup() {
|
|
|
3607
3609
|
sendsSinceLastListen = 0;
|
|
3608
3610
|
sendLimit = 10;
|
|
3609
3611
|
touchHeartbeat(registeredName);
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
});
|
|
3612
|
+
// Minimal empty-batch response — the EMPTY-RETURN RULE is already in
|
|
3613
|
+
// every agent's guide + AGENTS.md block, so there's no need to repeat
|
|
3614
|
+
// the "this is normal, call again" reminder every 90s. Trimmed to the
|
|
3615
|
+
// irreducible payload so long listen loops cost as few tokens as
|
|
3616
|
+
// possible. Over a full session this saves ~2 tokens per wake-up *
|
|
3617
|
+
// hundreds of wake-ups = meaningful savings on long-running agents.
|
|
3618
|
+
resolve({ messages: [], retry: true });
|
|
3618
3619
|
}
|
|
3619
3620
|
};
|
|
3620
3621
|
|
|
@@ -3707,101 +3708,41 @@ function buildListenGroupResponse(batch, consumed, agentName, listenStart) {
|
|
|
3707
3708
|
return new Date(a.timestamp) - new Date(b.timestamp);
|
|
3708
3709
|
});
|
|
3709
3710
|
|
|
3710
|
-
//
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
const key = `${m.from}:${type}`;
|
|
3716
|
-
summaryCounts[key] = (summaryCounts[key] || 0) + 1;
|
|
3717
|
-
}
|
|
3718
|
-
const summaryParts = [];
|
|
3719
|
-
for (const [key, count] of Object.entries(summaryCounts)) {
|
|
3720
|
-
const [from, type] = key.split(':');
|
|
3721
|
-
summaryParts.push(`${count} ${type} from ${from}`);
|
|
3722
|
-
}
|
|
3723
|
-
const batchSummary = `${batch.length} messages: ${summaryParts.join(', ')}`;
|
|
3724
|
-
|
|
3725
|
-
// Agent statuses — lightweight, no history reads. Uses the recency grace
|
|
3726
|
-
// so peers that just briefly returned from listen_group() to process a
|
|
3727
|
-
// batch still read as "listening", not "working".
|
|
3728
|
-
const agents = getAgents();
|
|
3729
|
-
const agentNames = Object.keys(agents).filter(n => isPidAlive(agents[n].pid, agents[n].last_activity));
|
|
3730
|
-
const agentStatus = {};
|
|
3731
|
-
for (const n of agentNames) {
|
|
3732
|
-
if (isRecentlyListening(agents[n])) {
|
|
3733
|
-
agentStatus[n] = 'listening';
|
|
3734
|
-
} else {
|
|
3735
|
-
const lastListened = agents[n].last_listened_at;
|
|
3736
|
-
const sinceLastListen = lastListened ? Date.now() - new Date(lastListened).getTime() : Infinity;
|
|
3737
|
-
agentStatus[n] = sinceLastListen > 120000 ? 'unresponsive' : 'working';
|
|
3738
|
-
}
|
|
3739
|
-
}
|
|
3740
|
-
|
|
3741
|
-
const now = Date.now();
|
|
3711
|
+
// LEAN RESPONSE (v5.5.3+): the agent already has every prior message in its
|
|
3712
|
+
// own LLM context, plus the full rule set from get_guide() + AGENTS.md. We
|
|
3713
|
+
// only send the NEW messages + the managed-mode signals needed for
|
|
3714
|
+
// turn-taking. No repeated reminders, no agent rosters, no "next_action"
|
|
3715
|
+
// — agents already know what to do from their guide.
|
|
3742
3716
|
const result = {
|
|
3743
3717
|
messages: batch.map(m => {
|
|
3744
|
-
const ageSec = Math.round((now - new Date(m.timestamp).getTime()) / 1000);
|
|
3745
3718
|
const isOwnerMsg = m.from === 'Dashboard' || m.from === 'Owner' || m.from === 'dashboard' || m.from === 'owner';
|
|
3746
3719
|
return {
|
|
3747
3720
|
id: m.id, from: m.from, to: m.to, content: m.content,
|
|
3748
3721
|
timestamp: m.timestamp,
|
|
3749
|
-
age_seconds: ageSec,
|
|
3750
|
-
...(ageSec > 30 && { delayed: true }),
|
|
3751
3722
|
...(m.reply_to && { reply_to: m.reply_to }),
|
|
3752
3723
|
...(m.thread_id && { thread_id: m.thread_id }),
|
|
3753
3724
|
...(m.addressed_to && { addressed_to: m.addressed_to }),
|
|
3754
|
-
...(
|
|
3755
|
-
addressed_to_you: !m.addressed_to || m.addressed_to.includes(agentName),
|
|
3756
|
-
should_respond: !m.addressed_to || m.addressed_to.includes(agentName),
|
|
3757
|
-
}),
|
|
3758
|
-
...(isOwnerMsg && {
|
|
3759
|
-
from_owner: true,
|
|
3760
|
-
system_instruction: 'OWNER MESSAGE. You MUST reply by calling send_message(to="Dashboard", content="your reply") — the owner reads replies ONLY in the dashboard Messages tab. Any text you write in your CLI terminal is INVISIBLE to the owner and does not count as a reply. After send_message, call listen_group() again immediately.',
|
|
3761
|
-
}),
|
|
3725
|
+
...(isOwnerMsg && { from_owner: true }),
|
|
3762
3726
|
};
|
|
3763
3727
|
}),
|
|
3764
|
-
message_count: batch.length,
|
|
3765
|
-
batch_summary: batchSummary,
|
|
3766
|
-
agents_online: agentNames.length,
|
|
3767
|
-
agents_status: agentStatus,
|
|
3768
3728
|
};
|
|
3769
3729
|
|
|
3770
|
-
// Managed mode:
|
|
3730
|
+
// Managed mode: minimal turn-taking signal. Managers need to know the
|
|
3731
|
+
// floor/phase to decide next yield_floor(); participants need to know if
|
|
3732
|
+
// they hold the floor. The managed-mode RULE TEXT is already in the guide
|
|
3733
|
+
// so we don't repeat it per call.
|
|
3771
3734
|
if (isManagedMode()) {
|
|
3772
3735
|
const managed = getManagedConfig();
|
|
3773
|
-
const youHaveFloor = managed.turn_current === agentName;
|
|
3774
|
-
const youAreManager = managed.manager === agentName;
|
|
3775
|
-
|
|
3776
3736
|
result.managed_context = {
|
|
3777
|
-
phase: managed.phase,
|
|
3778
|
-
|
|
3737
|
+
phase: managed.phase,
|
|
3738
|
+
floor: managed.floor,
|
|
3739
|
+
manager: managed.manager,
|
|
3740
|
+
you_have_floor: managed.turn_current === agentName,
|
|
3741
|
+
you_are_manager: managed.manager === agentName,
|
|
3779
3742
|
turn_current: managed.turn_current,
|
|
3780
3743
|
};
|
|
3781
|
-
|
|
3782
|
-
if (youAreManager) {
|
|
3783
|
-
result.should_respond = true;
|
|
3784
|
-
result.instructions = 'You are the MANAGER. Decide who speaks next using yield_floor(), or advance the phase using set_phase().';
|
|
3785
|
-
} else if (youHaveFloor) {
|
|
3786
|
-
result.should_respond = true;
|
|
3787
|
-
result.instructions = 'It is YOUR TURN to speak. Respond now, then the floor will return to the manager.';
|
|
3788
|
-
} else if (managed.floor === 'execution') {
|
|
3789
|
-
result.should_respond = false;
|
|
3790
|
-
result.instructions = `EXECUTION PHASE: Focus on your assigned tasks. Only message the manager (${managed.manager}) if you need help or to report completion.`;
|
|
3791
|
-
} else {
|
|
3792
|
-
result.should_respond = false;
|
|
3793
|
-
result.instructions = 'DO NOT RESPOND. Wait for the manager to give you the floor. Call listen() again to wait.';
|
|
3794
|
-
}
|
|
3795
3744
|
}
|
|
3796
3745
|
|
|
3797
|
-
const fromDashboard = Array.isArray(batch) && batch.some(m => m && (m.from === 'Dashboard' || m.from === 'Owner' || m.from === 'dashboard' || m.from === 'owner'));
|
|
3798
|
-
const dashboardReplyHint = fromDashboard
|
|
3799
|
-
? ' One of these messages is from Dashboard/Owner — reply via send_message(to="Dashboard") so the owner sees your reply in the dashboard Messages tab. Do NOT narrate the reply in your CLI terminal; terminal output is invisible to the owner.'
|
|
3800
|
-
: '';
|
|
3801
|
-
result.next_action = (isAutonomousMode()
|
|
3802
|
-
? 'Process these messages, then call get_work() to continue the proactive work loop. Do NOT call listen_group() — use get_work() instead.'
|
|
3803
|
-
: 'After processing these messages and sending your response, call listen_group() again immediately. Never stop listening.') + dashboardReplyHint;
|
|
3804
|
-
|
|
3805
3746
|
const listenSurface = isManagedMode() && result.managed_context && result.managed_context.you_are_manager
|
|
3806
3747
|
? 'manager_listen'
|
|
3807
3748
|
: (isManagedMode() ? 'participant_listen' : 'team_listen');
|
|
@@ -8195,7 +8136,7 @@ function toolToggleRule(ruleId) {
|
|
|
8195
8136
|
// --- MCP Server setup ---
|
|
8196
8137
|
|
|
8197
8138
|
const server = new Server(
|
|
8198
|
-
{ name: 'agent-bridge', version: '5.
|
|
8139
|
+
{ name: 'agent-bridge', version: '5.5.3' },
|
|
8199
8140
|
{ capabilities: { tools: {} } }
|
|
8200
8141
|
);
|
|
8201
8142
|
|
|
@@ -9324,7 +9265,7 @@ async function main() {
|
|
|
9324
9265
|
try {
|
|
9325
9266
|
const transport = new StdioServerTransport();
|
|
9326
9267
|
await server.connect(transport);
|
|
9327
|
-
console.error('Agent Bridge MCP server v5.
|
|
9268
|
+
console.error('Agent Bridge MCP server v5.5.3 running (65 tools)');
|
|
9328
9269
|
} catch (e) {
|
|
9329
9270
|
console.error('ERROR: MCP server failed to start: ' + e.message);
|
|
9330
9271
|
console.error('Fix: Run "npx let-them-talk doctor" to check your setup.');
|
package/state/canonical.js
CHANGED
|
@@ -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,
|