let-them-talk 5.4.0 → 5.4.1
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 +281 -93
- package/USAGE.md +1 -1
- package/cli.js +1 -1
- package/dashboard.js +42 -1
- package/office/index.js +43 -40
- package/package.json +1 -1
- package/server.js +37 -6
package/README.md
CHANGED
|
@@ -1,158 +1,346 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="logo.png" alt="Let Them Talk" width="
|
|
2
|
+
<img src="logo.png" alt="Let Them Talk" width="140">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">Let Them Talk</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
|
|
8
|
+
<strong>Let your AI agents actually work as a team.</strong><br>
|
|
9
|
+
Multi-agent collaboration for Claude Code, Gemini CLI, Codex CLI, Ollama, and API-backed agents — with a live operator dashboard and a 3D virtual office to watch it all happen.
|
|
9
10
|
</p>
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://www.npmjs.com/package/let-them-talk"><img src="https://img.shields.io/npm/v/let-them-talk.svg?style=flat&color=58a6ff" alt="npm version"></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/let-them-talk"><img src="https://img.shields.io/npm/dm/let-them-talk.svg?style=flat&color=3fb950" alt="npm downloads"></a>
|
|
15
|
+
<a href="https://github.com/Dekelelz/let-them-talk/blob/master/LICENSE"><img src="https://img.shields.io/badge/License-BSL%201.1-f59e0b.svg?style=flat" alt="BSL 1.1"></a>
|
|
16
|
+
<a href="https://discord.gg/6Y9YgkFNJP"><img src="https://img.shields.io/discord/1482478651000885359?color=5865F2&label=Discord&logo=discord&logoColor=white&style=flat" alt="Discord"></a>
|
|
17
|
+
<a href="https://nodejs.org/"><img src="https://img.shields.io/node/v/let-them-talk.svg?color=3fb950&style=flat" alt="Node.js"></a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<p align="center">
|
|
21
|
+
<a href="https://talk.unrealai.studio">Website</a> ·
|
|
22
|
+
<a href="#-quick-start">Quick Start</a> ·
|
|
23
|
+
<a href="#-features">Features</a> ·
|
|
24
|
+
<a href="#-installation">Install</a> ·
|
|
25
|
+
<a href="#-dashboard-tour">Dashboard</a> ·
|
|
26
|
+
<a href="#-core-concepts">Concepts</a> ·
|
|
27
|
+
<a href="#-architecture">Architecture</a> ·
|
|
28
|
+
<a href="https://discord.gg/6Y9YgkFNJP">Discord</a>
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## What it is
|
|
34
|
+
|
|
35
|
+
Let Them Talk is a **local MCP broker and operator dashboard** that lets multiple AI CLI agents share one project runtime. Open Claude Code, Gemini CLI, or Codex CLI in separate terminals — they discover each other, exchange messages, assign tasks, review each other's work, coordinate through workflows, and coordinate branches, sessions, and evidence through a shared `.agent-bridge/` directory. A browser dashboard gives you real-time visibility with 12 tabs — including a 3D virtual office where chibi agent characters walk between desks, wave during broadcasts, and sleep when idle.
|
|
36
|
+
|
|
37
|
+
If you want your agents to stop working in isolation and start collaborating like a real team, this is it.
|
|
12
38
|
|
|
13
|
-
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🚀 Quick Start
|
|
14
42
|
|
|
15
43
|
```bash
|
|
44
|
+
# 1. Configure the MCP broker for every installed CLI (Claude / Gemini / Codex)
|
|
16
45
|
npx let-them-talk init
|
|
46
|
+
|
|
47
|
+
# 2. Launch the web dashboard (localhost:3000)
|
|
17
48
|
node .agent-bridge/launch.js
|
|
18
49
|
```
|
|
19
50
|
|
|
20
|
-
|
|
51
|
+
Now open your CLI in a second terminal and tell it to join:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
You are "Alice". Call register("Alice","Claude"), then get_briefing(),
|
|
55
|
+
then listen_group() and stay in the loop.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Open a third terminal, tell that agent to register as `Bob`, and the two will start talking. Everything is visible in the dashboard Messages tab, and you can reply directly from there.
|
|
59
|
+
|
|
60
|
+
> **Skip the manual prompts** with `npx let-them-talk init --template team` — gives you Coordinator + Researcher + Coder prompts ready to paste.
|
|
61
|
+
|
|
62
|
+
---
|
|
21
63
|
|
|
22
|
-
|
|
23
|
-
2. Call `get_briefing()` if you are joining existing work.
|
|
24
|
-
3. Use `listen()` in direct mode, `listen_group()` in group or managed mode, or `get_work()` if you are running the proactive autonomy loop.
|
|
64
|
+
## ⚡ Why Let Them Talk
|
|
25
65
|
|
|
26
|
-
|
|
66
|
+
| Without Let Them Talk | With Let Them Talk |
|
|
67
|
+
|---|---|
|
|
68
|
+
| One agent works, you copy-paste context to the next | Agents share one runtime and see each other's work automatically |
|
|
69
|
+
| "Done" is just a message that says "done" | Completion requires structured evidence (summary, verification, files_changed, confidence) |
|
|
70
|
+
| You babysit the loop all day | `get_work` / `verify_and_advance` + autonomy-v2 run the loop for you |
|
|
71
|
+
| No visibility into what agents are doing | Dashboard with Messages, Tasks, Workflows, Graph, Plan, 3D Hub |
|
|
72
|
+
| Provider lock-in | Claude Code, Gemini CLI, Codex CLI, Ollama, and custom API agents all first-class |
|
|
73
|
+
| Coordination is "chat" | Branches are full execution contexts. Sessions are branch-scoped. Governance is event-backed. |
|
|
27
74
|
|
|
28
|
-
|
|
29
|
-
- Legacy JSON and JSONL files remain compatibility projections during migration. They are not the authority model.
|
|
30
|
-
- The runtime contract treats branches as full-context namespaces. In the shipped runtime today, branch-local guarantees already cover messages and history, delivery and read state, conversation control and non-general channels, sessions, evidence, tasks and workflows, and workspaces.
|
|
31
|
-
- Branch-local guarantees now also cover the governance surfaces that used to remain compatibility-shared during migration: decisions, KB, reviews, dependencies, votes, rules, and progress.
|
|
32
|
-
- Branch switches replace the whole migrated branch-local collaboration view at once.
|
|
33
|
-
- Sessions are tied to one agent on one branch. Switching branches suspends one branch session and creates or resumes another, and forks copy historical session and evidence context without cloning live execution.
|
|
34
|
-
- Terminal task and workflow completion is only authoritative when structured evidence is recorded, including `recorded_at` and `recorded_by_session` metadata.
|
|
35
|
-
- Markdown workspace export writes to `.agent-bridge-markdown/` and stays non-authoritative. Editing exported markdown does not change runtime state.
|
|
75
|
+
---
|
|
36
76
|
|
|
37
|
-
|
|
77
|
+
## ✨ Features
|
|
38
78
|
|
|
39
|
-
- `
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
- `
|
|
44
|
-
- `
|
|
79
|
+
- **66 MCP tools** for the full coordination surface — `register`, `send_message`, `broadcast`, `listen_group`, `get_work`, `verify_and_advance`, `create_task`, `start_plan`, `advance_workflow`, `lock_file`, `log_decision`, `kb_write`, `call_vote`, `submit_review`, `handoff`, and 50+ more.
|
|
80
|
+
- **Canonical runtime** — event-backed state under `.agent-bridge/runtime/` with replay, projections, and branch-local isolation.
|
|
81
|
+
- **Branches as full execution contexts** — messages, tasks, workflows, sessions, evidence, governance (decisions, KB, reviews, votes, rules, progress) all switch together on a branch change.
|
|
82
|
+
- **Sessions + evidence-backed completion** — first-class session records; "done" is authoritative only when structured evidence is recorded (`summary`, `verification`, `files_changed`, `confidence`, `recorded_at`, `recorded_by_session`).
|
|
83
|
+
- **Explicit runtime descriptors** — `runtime_type`, `provider_id`, `model_id`, `capabilities` (chat, vision, image_generation, video_generation, texture_generation). Mixed-provider teams coordinate by capability, not guesswork.
|
|
84
|
+
- **Autonomy-v2** — `get_work` picks the next item using canonical state + sessions + evidence + capabilities + contracts. Watchdog with idle detection, retry policy, circuit breakers, and bounded escalation.
|
|
85
|
+
- **3D virtual office** — real-time chibi-style visualization of your team. Agents walk between desks, react to broadcasts, celebrate tasks, sleep when idle.
|
|
86
|
+
- **Web dashboard** — 12 tabs: 3D Hub, Messages, Tasks, Workspaces, Workflows, Graph, Plan, Launch, Rules, Stats, Services, Docs.
|
|
87
|
+
- **Managed mode** — structured turn-taking with a Manager agent (`claim_manager`, `yield_floor`, `set_phase`) — prevents 3+ agent chaos.
|
|
88
|
+
- **Channels** — sub-team communication without flooding `#general`.
|
|
89
|
+
- **Markdown workspace export** — Obsidian-friendly one-way export (`.agent-bridge-markdown/`), explicitly non-authoritative.
|
|
90
|
+
- **Grouped verification** — `verify:contracts`, `verify:replay`, `verify:invariants`, `verify:smoke` — script-driven, deterministic, dozens of invariants covered.
|
|
91
|
+
- **0-vulnerability dependencies** — only 2 direct deps (`@modelcontextprotocol/sdk`, `three`), every transitive pinned to a known-safe version.
|
|
45
92
|
|
|
46
|
-
|
|
93
|
+
---
|
|
47
94
|
|
|
48
|
-
|
|
95
|
+
## 📦 Installation
|
|
96
|
+
|
|
97
|
+
### Prerequisites
|
|
98
|
+
- [Node.js 18 or higher](https://nodejs.org/) — `node --version` to check
|
|
99
|
+
- One or more AI CLIs:
|
|
100
|
+
- [Claude Code](https://claude.ai/code)
|
|
101
|
+
- [Gemini CLI](https://github.com/google-gemini/gemini-cli)
|
|
102
|
+
- [Codex CLI](https://github.com/openai/codex)
|
|
103
|
+
|
|
104
|
+
### Init (auto-detect everything installed)
|
|
49
105
|
|
|
50
106
|
```bash
|
|
107
|
+
cd your-project
|
|
51
108
|
npx let-them-talk init
|
|
52
|
-
npx let-them-talk init --claude
|
|
53
|
-
npx let-them-talk init --gemini
|
|
54
|
-
npx let-them-talk init --codex
|
|
55
|
-
npx let-them-talk init --all
|
|
56
|
-
npx let-them-talk init --ollama
|
|
57
|
-
npx let-them-talk init --template <name>
|
|
58
109
|
```
|
|
59
110
|
|
|
60
|
-
|
|
111
|
+
### Init for a specific CLI
|
|
61
112
|
|
|
62
113
|
```bash
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
114
|
+
npx let-them-talk init --claude # Claude Code only
|
|
115
|
+
npx let-them-talk init --gemini # Gemini CLI only
|
|
116
|
+
npx let-them-talk init --codex # Codex CLI only
|
|
117
|
+
npx let-them-talk init --all # All three
|
|
118
|
+
npx let-them-talk init --ollama # Add a local Ollama bridge
|
|
68
119
|
```
|
|
69
120
|
|
|
70
|
-
|
|
121
|
+
### Init with a ready-made template
|
|
71
122
|
|
|
72
123
|
```bash
|
|
73
|
-
npx let-them-talk
|
|
74
|
-
npx let-them-talk
|
|
75
|
-
npx let-them-talk
|
|
76
|
-
npx let-them-talk
|
|
77
|
-
npx let-them-talk
|
|
124
|
+
npx let-them-talk init --template pair # 2-agent chat
|
|
125
|
+
npx let-them-talk init --template team # Coordinator + Researcher + Coder
|
|
126
|
+
npx let-them-talk init --template review # Author + Reviewer code-review pair
|
|
127
|
+
npx let-them-talk init --template debate # Pro + Con structured debate
|
|
128
|
+
npx let-them-talk init --template managed # Manager + Designer + Coder + Tester
|
|
78
129
|
```
|
|
79
130
|
|
|
80
|
-
|
|
131
|
+
### What init writes (all merge-safe)
|
|
81
132
|
|
|
82
|
-
|
|
133
|
+
- `.mcp.json` — Claude Code MCP config
|
|
134
|
+
- `.gemini/settings.json` — Gemini CLI MCP config
|
|
135
|
+
- `.codex/config.toml` — Codex CLI MCP config
|
|
136
|
+
- `AGENTS.md` / `CLAUDE.md` — background-worker rules block (marker-delimited, never clobbers your content)
|
|
137
|
+
- `.agent-bridge/launch.js` — local launcher (no re-download needed)
|
|
138
|
+
- `.gitignore` — adds sensible entries
|
|
83
139
|
|
|
84
|
-
- `
|
|
85
|
-
- `team`
|
|
86
|
-
- `review`
|
|
87
|
-
- `debate`
|
|
88
|
-
- `managed`
|
|
140
|
+
All existing configs are preserved — agent-bridge is added alongside your other MCP servers, with `.backup` files created before any edit.
|
|
89
141
|
|
|
90
|
-
|
|
142
|
+
### Launch the dashboard
|
|
91
143
|
|
|
92
|
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
144
|
+
```bash
|
|
145
|
+
node .agent-bridge/launch.js # localhost:3000
|
|
146
|
+
node .agent-bridge/launch.js --lan # also listen on LAN (phone/tablet)
|
|
147
|
+
node .agent-bridge/launch.js status # CLI status snapshot
|
|
148
|
+
node .agent-bridge/launch.js msg <agent> # send a message from the terminal
|
|
149
|
+
node .agent-bridge/launch.js migrate # backfill canonical events from legacy projects
|
|
150
|
+
```
|
|
97
151
|
|
|
98
|
-
|
|
152
|
+
---
|
|
99
153
|
|
|
100
|
-
|
|
154
|
+
## 🎬 The 60-second demo
|
|
101
155
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
156
|
+
```bash
|
|
157
|
+
# In project folder
|
|
158
|
+
npx let-them-talk init --template team
|
|
159
|
+
node .agent-bridge/launch.js
|
|
160
|
+
```
|
|
106
161
|
|
|
107
|
-
|
|
162
|
+
Open three terminals. The `templates` output prints the exact prompt to paste into each:
|
|
108
163
|
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
- `video_generation`
|
|
113
|
-
- `texture_generation`
|
|
164
|
+
- **Terminal 1 (Coordinator):** receives the user's request, breaks it into tasks, delegates to Researcher and Coder.
|
|
165
|
+
- **Terminal 2 (Researcher):** reads code, searches patterns, reports findings to Coordinator.
|
|
166
|
+
- **Terminal 3 (Coder):** implements, reports summary + verification + files_changed back.
|
|
114
167
|
|
|
115
|
-
|
|
168
|
+
From the dashboard Messages tab, send the Coordinator a task. Watch the team execute it across all three terminals, with every message, task transition, workflow step, and evidence record live on screen. The 3D Hub shows chibi versions of your agents walking to their desks and typing when working.
|
|
116
169
|
|
|
117
|
-
|
|
170
|
+
---
|
|
118
171
|
|
|
119
|
-
|
|
120
|
-
|
|
172
|
+
## 🎛️ Dashboard tour
|
|
173
|
+
|
|
174
|
+
| Tab | What it does |
|
|
175
|
+
|---|---|
|
|
176
|
+
| **3D Hub** | Live chibi-style visualization of your team. Per-project worlds, buildings, behaviors. |
|
|
177
|
+
| **Messages** | Full conversation timeline with threading, reactions, pinning, search, and direct reply-to-Dashboard. |
|
|
178
|
+
| **Tasks** | Kanban of all tasks across the branch. Drag to change status. Evidence-backed completion. **Clear All Tasks** button for cleanup. |
|
|
179
|
+
| **Workspaces** | Per-agent scratchpad. Other agents can read, only you can write. 50 keys, 100 KB values. |
|
|
180
|
+
| **Workflows** | Multi-step plans with dependencies, parallel steps, and auto-advance on verify. |
|
|
181
|
+
| **Graph** | Agent/task/dependency network view. |
|
|
182
|
+
| **Plan** | Live autonomous-plan progress with pause/stop/skip/reassign controls. |
|
|
183
|
+
| **Launch** | Start agents directly from the dashboard (Add Project initializes the target folder for you). |
|
|
184
|
+
| **Rules** | Project-wide rules injected into every agent's guide. |
|
|
185
|
+
| **Stats** | Messages, tasks, completion rates, per-agent activity. |
|
|
186
|
+
| **Services** | Status of configured providers and API keys. |
|
|
187
|
+
| **Docs** | Shipped architecture + usage docs, searchable. |
|
|
188
|
+
|
|
189
|
+
The dashboard also supports:
|
|
190
|
+
- Saved named layouts
|
|
191
|
+
- Omnibox / command palette on the search bar
|
|
192
|
+
- Per-project branch switching and Clear Messages (canonical-aware)
|
|
193
|
+
- **Reinstall Providers** — rewrites per-project MCP configs and refreshes the `AGENTS.md` rule block without touching your other content
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 📐 Core concepts
|
|
198
|
+
|
|
199
|
+
### Runtime
|
|
200
|
+
|
|
201
|
+
- **Canonical truth** lives in an event-backed runtime under `.agent-bridge/runtime/`.
|
|
202
|
+
- Legacy flat `.json` / `.jsonl` files in `.agent-bridge/` are compatibility projections during migration — not the authority model.
|
|
203
|
+
- All mutations go through a shared canonical facade (`state/canonical.js`). The dashboard is a client of the broker, not a second writer.
|
|
204
|
+
|
|
205
|
+
### Branches
|
|
206
|
+
|
|
207
|
+
Branches are **full execution contexts**, not just message logs. A branch switch replaces the migrated branch-local view all at once:
|
|
208
|
+
- messages and history
|
|
209
|
+
- delivery and read state
|
|
210
|
+
- conversation control and non-general channels
|
|
211
|
+
- tasks and workflows
|
|
212
|
+
- workspaces
|
|
213
|
+
- sessions and evidence
|
|
214
|
+
- governance: decisions, KB, reviews, dependencies, votes, rules, progress
|
|
215
|
+
|
|
216
|
+
Branch creation snapshots the source branch at the fork point. Branch-local changes never bleed into `main` until explicitly advanced.
|
|
217
|
+
|
|
218
|
+
### Sessions + evidence
|
|
219
|
+
|
|
220
|
+
Sessions are branch-scoped records of one agent's work on one branch. Rejoining the same branch resumes that branch-scoped context. Forks carry historical session and evidence context but do not clone live execution.
|
|
221
|
+
|
|
222
|
+
Completion is authoritative only when structured evidence is recorded:
|
|
223
|
+
- `summary`
|
|
224
|
+
- `verification`
|
|
225
|
+
- `files_changed`
|
|
226
|
+
- `confidence` (0–100)
|
|
227
|
+
- `recorded_at`
|
|
228
|
+
- `recorded_by_session`
|
|
229
|
+
|
|
230
|
+
Anything less is a conversational "done", not a runtime "done".
|
|
231
|
+
|
|
232
|
+
### Providers + capabilities
|
|
233
|
+
|
|
234
|
+
Every agent has an explicit runtime descriptor:
|
|
235
|
+
- `runtime_type` (CLI / API / custom)
|
|
236
|
+
- `provider_id` (Claude / Codex / Gemini / Ollama / ...)
|
|
237
|
+
- `model_id`
|
|
238
|
+
- `capabilities` — tokens like `chat`, `vision`, `image_generation`, `video_generation`, `texture_generation`
|
|
239
|
+
|
|
240
|
+
Coordinators can route work by capability instead of by heuristic — `get_work` and task assignment both respect declared capabilities.
|
|
241
|
+
|
|
242
|
+
### Autonomy loop
|
|
243
|
+
|
|
244
|
+
Instead of babysitting the chat:
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
Coordinator → start_plan(name, steps, assignees)
|
|
248
|
+
↓
|
|
249
|
+
Each agent → get_work() → do work → verify_and_advance() → get_work() → ...
|
|
121
250
|
```
|
|
122
251
|
|
|
123
|
-
|
|
252
|
+
- **`get_work`** picks the highest-priority item from: assigned workflow step, claimable task, open review, help request, blocked dependency, and more.
|
|
253
|
+
- **`verify_and_advance`** self-verifies with evidence. ≥ 70 confidence auto-advances. 40–69 advances with a flag. < 40 broadcasts a help request.
|
|
254
|
+
- **`retry_with_improvement`** handles failures. 3 failed retries auto-escalate to the team. Skill accumulation is stored in the KB for everyone.
|
|
255
|
+
- **Watchdog** detects idle agents, stuck steps, and dead owners. Can rotate ownership within bounds.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 🧩 Agent templates
|
|
124
260
|
|
|
125
|
-
|
|
261
|
+
### Agent templates (role prompts)
|
|
126
262
|
|
|
127
|
-
|
|
263
|
+
| Template | Agents | Use when |
|
|
264
|
+
|---|---|---|
|
|
265
|
+
| `pair` | A, B | Two-agent brainstorm or Q&A |
|
|
266
|
+
| `team` | Coordinator, Researcher, Coder | Feature work with research + implementation |
|
|
267
|
+
| `review` | Author, Reviewer | Code-review loop |
|
|
268
|
+
| `debate` | Pro, Con | Explore tradeoffs / architecture decisions |
|
|
269
|
+
| `managed` | Manager, Designer, Coder, Tester | 3+ agents with structured turn-taking |
|
|
128
270
|
|
|
129
|
-
|
|
271
|
+
### Conversation templates (workflow skeletons)
|
|
272
|
+
|
|
273
|
+
| Template | Purpose |
|
|
274
|
+
|---|---|
|
|
275
|
+
| `feature-build` | End-to-end feature: research → design → implement → test |
|
|
276
|
+
| `code-review` | Structured code review with evidence |
|
|
277
|
+
| `debug-squad` | Coordinated bug triage and fix |
|
|
278
|
+
| `research-write` | Research → synthesize → document |
|
|
279
|
+
| `autonomous-feature` | Fully autonomous multi-agent feature build |
|
|
280
|
+
|
|
281
|
+
List, show, or apply templates:
|
|
130
282
|
|
|
131
283
|
```bash
|
|
132
|
-
|
|
284
|
+
npx let-them-talk templates # list all
|
|
285
|
+
npx let-them-talk init --template team # scaffold a team
|
|
133
286
|
```
|
|
134
287
|
|
|
135
|
-
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## 🧪 Verification
|
|
291
|
+
|
|
292
|
+
Script-driven, deterministic, no flake:
|
|
136
293
|
|
|
137
294
|
```bash
|
|
138
|
-
npm
|
|
139
|
-
npm run verify
|
|
140
|
-
npm run verify:
|
|
141
|
-
npm run verify:
|
|
142
|
-
npm run verify:
|
|
295
|
+
npm test # delegates to verify
|
|
296
|
+
npm run verify # full suite
|
|
297
|
+
npm run verify:contracts # runtime + schema + branches + markdown
|
|
298
|
+
npm run verify:replay # event replay (healthy + clean + negative)
|
|
299
|
+
npm run verify:invariants # dashboard, capabilities, parity, sessions, evidence, autonomy, hooks
|
|
300
|
+
npm run verify:smoke # representative subset
|
|
143
301
|
```
|
|
144
302
|
|
|
145
|
-
|
|
303
|
+
The verify suite doesn't claim to cover every provider or runtime matrix, and does not include browser automation. But every shipped invariant is script-enforced on every release.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## 🔐 Security
|
|
308
|
+
|
|
309
|
+
- **Dashboard binds to `127.0.0.1` by default.** LAN mode (`--lan`) requires explicit enablement and uses a file-based auth token.
|
|
310
|
+
- **Rate-limited** API endpoints on non-localhost requests.
|
|
311
|
+
- **No telemetry, no cloud.** Everything runs locally.
|
|
312
|
+
- **0 known vulnerabilities** in the shipped tarball as of v5.4.1.
|
|
313
|
+
- **Sensitive-path blocks** on file-share: `.env`, `.pem`, `.key`, `.lan-token`, `mcp.json`, and the agent-bridge data directory cannot be shared.
|
|
314
|
+
- See [`SECURITY.md`](SECURITY.md) for the disclosure policy.
|
|
315
|
+
|
|
316
|
+
---
|
|
146
317
|
|
|
147
|
-
|
|
148
|
-
- `verify:replay` checks healthy and clean replay plus expected-failure negative replay scenarios.
|
|
149
|
-
- `verify:invariants` checks authority routing, dashboard control plane behavior, performance and indexing, provider capabilities, API-agent parity, dashboard semantic-gap coverage, migration hardening, branch isolation, session lifecycle, evidence-backed completion, session-aware context, autonomy v2, advisory contracts, managed-team integration, lifecycle hooks, and markdown workspace export and safety.
|
|
150
|
-
- `verify:smoke` runs a representative subset, including the dashboard semantic-gap check.
|
|
318
|
+
## 📚 Architecture
|
|
151
319
|
|
|
152
|
-
|
|
320
|
+
Source-of-truth docs:
|
|
153
321
|
|
|
154
|
-
|
|
322
|
+
- [`docs/architecture/runtime-contract.md`](docs/architecture/runtime-contract.md)
|
|
323
|
+
- [`docs/architecture/branch-semantics.md`](docs/architecture/branch-semantics.md)
|
|
324
|
+
- [`docs/architecture/canonical-event-schema.md`](docs/architecture/canonical-event-schema.md)
|
|
325
|
+
- [`docs/architecture/markdown-workspace.md`](docs/architecture/markdown-workspace.md)
|
|
326
|
+
- [`docs/architecture/runtime-migration-hardening.md`](docs/architecture/runtime-migration-hardening.md)
|
|
155
327
|
|
|
156
|
-
|
|
328
|
+
---
|
|
157
329
|
|
|
158
|
-
|
|
330
|
+
## 💬 Community
|
|
331
|
+
|
|
332
|
+
- [Discord](https://discord.gg/6Y9YgkFNJP) — questions, show-and-tell, feedback
|
|
333
|
+
- [GitHub Issues](https://github.com/Dekelelz/let-them-talk/issues) — bugs and feature requests
|
|
334
|
+
- [Website](https://talk.unrealai.studio) — project home
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## 📄 License
|
|
339
|
+
|
|
340
|
+
[Business Source License 1.1](LICENSE). See the license file for usage terms.
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
<p align="center">
|
|
345
|
+
<sub>Built for humans who want their AI agents to work as a team.</sub>
|
|
346
|
+
</p>
|
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.4.
|
|
3
|
+
# Let Them Talk Usage Guide v5.4.1
|
|
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.4.
|
|
12
|
+
Let Them Talk — Agent Bridge v5.4.1
|
|
13
13
|
MCP message broker for inter-agent communication
|
|
14
14
|
Supports: Claude Code, Gemini CLI, Codex CLI, Ollama
|
|
15
15
|
|
package/dashboard.js
CHANGED
|
@@ -202,7 +202,45 @@ function isRecentlyListening(info) {
|
|
|
202
202
|
return Date.now() - last < LISTEN_RECENCY_GRACE_MS;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
// Virtual-agent helpers. "Dashboard" and "Owner" represent the operator UI,
|
|
206
|
+
// not a real CLI process. Writing them to agents.json lets list_agents and
|
|
207
|
+
// list_channels show them as first-class recipients, so agents can DM the
|
|
208
|
+
// operator via send_message(to="Dashboard") without confusion.
|
|
209
|
+
const VIRTUAL_AGENT_NAMES = ['Dashboard', 'Owner'];
|
|
210
|
+
function ensureVirtualAgents(projectPath) {
|
|
211
|
+
try {
|
|
212
|
+
const dataDir = resolveDataDir(projectPath);
|
|
213
|
+
if (!fs.existsSync(dataDir)) return;
|
|
214
|
+
const agentsFile = path.join(dataDir, 'agents.json');
|
|
215
|
+
let agents = {};
|
|
216
|
+
if (fs.existsSync(agentsFile)) {
|
|
217
|
+
try { agents = JSON.parse(fs.readFileSync(agentsFile, 'utf8')); } catch {}
|
|
218
|
+
}
|
|
219
|
+
const now = new Date().toISOString();
|
|
220
|
+
let changed = false;
|
|
221
|
+
for (const name of VIRTUAL_AGENT_NAMES) {
|
|
222
|
+
if (!agents[name] || !agents[name].is_virtual) {
|
|
223
|
+
agents[name] = {
|
|
224
|
+
pid: -1,
|
|
225
|
+
is_virtual: true,
|
|
226
|
+
virtual_type: 'owner',
|
|
227
|
+
timestamp: (agents[name] && agents[name].timestamp) || now,
|
|
228
|
+
last_activity: now,
|
|
229
|
+
last_listened_at: now,
|
|
230
|
+
provider: 'Dashboard',
|
|
231
|
+
branch: 'main',
|
|
232
|
+
};
|
|
233
|
+
changed = true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (changed) {
|
|
237
|
+
fs.writeFileSync(agentsFile, JSON.stringify(agents, null, 2) + '\n');
|
|
238
|
+
}
|
|
239
|
+
} catch {} // best-effort — if agents.json is locked, we'll try again next request
|
|
240
|
+
}
|
|
241
|
+
|
|
205
242
|
function isPidAlive(pid, lastActivity) {
|
|
243
|
+
if (pid === -1) return true; // virtual agents (Dashboard, Owner) — always alive
|
|
206
244
|
const STALE_THRESHOLD = 60000; // 60s — if heartbeat updated within this, agent is alive
|
|
207
245
|
|
|
208
246
|
// PRIORITY 1: Trust heartbeat freshness over PID status
|
|
@@ -270,6 +308,9 @@ function apiChannels(query) {
|
|
|
270
308
|
|
|
271
309
|
function apiAgents(query) {
|
|
272
310
|
const projectPath = query.get('project') || null;
|
|
311
|
+
// Make sure the operator virtual agents (Dashboard, Owner) exist so agents
|
|
312
|
+
// querying list_agents over MCP see them as valid DM recipients.
|
|
313
|
+
ensureVirtualAgents(projectPath);
|
|
273
314
|
const canonicalState = getCanonicalState(projectPath);
|
|
274
315
|
const agents = canonicalState.listAgents();
|
|
275
316
|
const profiles = canonicalState.listProfiles();
|
|
@@ -3417,7 +3458,7 @@ server.listen(PORT, LAN_MODE ? '0.0.0.0' : '127.0.0.1', () => {
|
|
|
3417
3458
|
const dataDir = resolveDataDir();
|
|
3418
3459
|
const lanIP = getLanIP();
|
|
3419
3460
|
console.log('');
|
|
3420
|
-
console.log(' Let Them Talk - Agent Bridge Dashboard v5.4.
|
|
3461
|
+
console.log(' Let Them Talk - Agent Bridge Dashboard v5.4.1');
|
|
3421
3462
|
console.log(' ============================================');
|
|
3422
3463
|
console.log(' Dashboard: http://localhost:' + PORT);
|
|
3423
3464
|
if (LAN_MODE && lanIP) {
|
package/office/index.js
CHANGED
|
@@ -28,13 +28,13 @@ function getCityMods() {
|
|
|
28
28
|
}).catch(function(e) { console.warn('City modules failed:', e); });
|
|
29
29
|
return _cityMods;
|
|
30
30
|
}
|
|
31
|
-
function isDriving() { return _cityMods && _cityMods.vehicle && _cityMods.vehicle.isDriving(); }
|
|
32
|
-
function isConnected() { return false; }
|
|
33
|
-
|
|
34
|
-
function scopedOfficeApiUrl(path, options) {
|
|
35
|
-
if (typeof window.scopedApiUrl === 'function') return window.scopedApiUrl(path, null, options);
|
|
36
|
-
return path;
|
|
37
|
-
}
|
|
31
|
+
function isDriving() { return _cityMods && _cityMods.vehicle && _cityMods.vehicle.isDriving(); }
|
|
32
|
+
function isConnected() { return false; }
|
|
33
|
+
|
|
34
|
+
function scopedOfficeApiUrl(path, options) {
|
|
35
|
+
if (typeof window.scopedApiUrl === 'function') return window.scopedApiUrl(path, null, options);
|
|
36
|
+
return path;
|
|
37
|
+
}
|
|
38
38
|
|
|
39
39
|
// Expose createCharacter + resolveAppearance for the character designer (Phase 3)
|
|
40
40
|
export { createCharacter } from './character.js';
|
|
@@ -319,29 +319,29 @@ function executeCommand(agentName, action) {
|
|
|
319
319
|
});
|
|
320
320
|
break;
|
|
321
321
|
|
|
322
|
-
case 'send_message':
|
|
323
|
-
showInputOverlay('Send message to ' + agentName + ':', 'Type your message...', function(msg) {
|
|
324
|
-
if (msg && msg.trim()) {
|
|
325
|
-
showBubble(agent, 'Message incoming...');
|
|
326
|
-
fetch(scopedOfficeApiUrl('/api/inject'), {
|
|
327
|
-
method: 'POST',
|
|
328
|
-
headers: { 'Content-Type': 'application/json', 'X-LTT-Request': '1' },
|
|
329
|
-
body: JSON.stringify({ to: agentName, content: msg.trim() })
|
|
330
|
-
}).then(function() { showBubble(agent, 'Got it!'); });
|
|
331
|
-
}
|
|
322
|
+
case 'send_message':
|
|
323
|
+
showInputOverlay('Send message to ' + agentName + ':', 'Type your message...', function(msg) {
|
|
324
|
+
if (msg && msg.trim()) {
|
|
325
|
+
showBubble(agent, 'Message incoming...');
|
|
326
|
+
fetch(scopedOfficeApiUrl('/api/inject'), {
|
|
327
|
+
method: 'POST',
|
|
328
|
+
headers: { 'Content-Type': 'application/json', 'X-LTT-Request': '1' },
|
|
329
|
+
body: JSON.stringify({ to: agentName, content: msg.trim() })
|
|
330
|
+
}).then(function() { showBubble(agent, 'Got it!'); });
|
|
331
|
+
}
|
|
332
332
|
});
|
|
333
333
|
break;
|
|
334
334
|
|
|
335
|
-
case 'assign_task':
|
|
336
|
-
showInputOverlay('New task for ' + agentName + ':', 'Task title...', function(title) {
|
|
337
|
-
if (title && title.trim()) {
|
|
338
|
-
showBubble(agent, 'New task assigned!');
|
|
339
|
-
fetch(scopedOfficeApiUrl('/api/tasks'), {
|
|
340
|
-
method: 'POST',
|
|
341
|
-
headers: { 'Content-Type': 'application/json', 'X-LTT-Request': '1' },
|
|
342
|
-
body: JSON.stringify({ title: title.trim(), assignee: agentName, status: 'pending' })
|
|
343
|
-
});
|
|
344
|
-
}
|
|
335
|
+
case 'assign_task':
|
|
336
|
+
showInputOverlay('New task for ' + agentName + ':', 'Task title...', function(title) {
|
|
337
|
+
if (title && title.trim()) {
|
|
338
|
+
showBubble(agent, 'New task assigned!');
|
|
339
|
+
fetch(scopedOfficeApiUrl('/api/tasks'), {
|
|
340
|
+
method: 'POST',
|
|
341
|
+
headers: { 'Content-Type': 'application/json', 'X-LTT-Request': '1' },
|
|
342
|
+
body: JSON.stringify({ title: title.trim(), assignee: agentName, status: 'pending' })
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
345
|
});
|
|
346
346
|
break;
|
|
347
347
|
|
|
@@ -355,13 +355,13 @@ function executeCommand(agentName, action) {
|
|
|
355
355
|
}
|
|
356
356
|
break;
|
|
357
357
|
|
|
358
|
-
case 'nudge':
|
|
359
|
-
showBubble(agent, 'Hey! Wake up!');
|
|
360
|
-
fetch(scopedOfficeApiUrl('/api/inject'), {
|
|
361
|
-
method: 'POST',
|
|
362
|
-
headers: { 'Content-Type': 'application/json', 'X-LTT-Request': '1' },
|
|
363
|
-
body: JSON.stringify({ to: agentName, content: 'Hey ' + agentName + ', the user is waiting for you. Please check for new messages and continue your work.' })
|
|
364
|
-
});
|
|
358
|
+
case 'nudge':
|
|
359
|
+
showBubble(agent, 'Hey! Wake up!');
|
|
360
|
+
fetch(scopedOfficeApiUrl('/api/inject'), {
|
|
361
|
+
method: 'POST',
|
|
362
|
+
headers: { 'Content-Type': 'application/json', 'X-LTT-Request': '1' },
|
|
363
|
+
body: JSON.stringify({ to: agentName, content: 'Hey ' + agentName + ', the user is waiting for you. Please check for new messages and continue your work.' })
|
|
364
|
+
});
|
|
365
365
|
break;
|
|
366
366
|
|
|
367
367
|
case 'edit_profile':
|
|
@@ -957,19 +957,20 @@ window.onPlayerSit = function(deskIdx) {
|
|
|
957
957
|
var iframeWrap = document.createElement('div');
|
|
958
958
|
iframeWrap.style.cssText = 'flex:1;position:relative;';
|
|
959
959
|
|
|
960
|
-
// Dashboard iframe
|
|
960
|
+
// Dashboard iframe. clipboard-write only — the iframe needs to copy agent
|
|
961
|
+
// prompts into the user's clipboard, it never needs to read from it.
|
|
961
962
|
var dashIframe = document.createElement('iframe');
|
|
962
963
|
dashIframe.id = 'mon-iframe-dashboard';
|
|
963
964
|
dashIframe.src = window.location.origin || 'http://localhost:3000';
|
|
964
965
|
dashIframe.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;border:none;background:#0d1117;';
|
|
965
|
-
dashIframe.allow = 'clipboard-
|
|
966
|
+
dashIframe.allow = 'clipboard-write';
|
|
966
967
|
iframeWrap.appendChild(dashIframe);
|
|
967
968
|
|
|
968
|
-
// ComfyUI iframe (hidden initially)
|
|
969
|
+
// ComfyUI iframe (hidden initially). Same write-only clipboard scope.
|
|
969
970
|
var comfyIframe = document.createElement('iframe');
|
|
970
971
|
comfyIframe.id = 'mon-iframe-comfyui';
|
|
971
972
|
comfyIframe.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;border:none;background:#1a1a2e;display:none;';
|
|
972
|
-
comfyIframe.allow = 'clipboard-
|
|
973
|
+
comfyIframe.allow = 'clipboard-write';
|
|
973
974
|
// Don't load ComfyUI until tab is clicked (saves resources)
|
|
974
975
|
iframeWrap.appendChild(comfyIframe);
|
|
975
976
|
|
|
@@ -995,9 +996,11 @@ window.onPlayerSit = function(deskIdx) {
|
|
|
995
996
|
if (dIframe) dIframe.style.display = 'none';
|
|
996
997
|
if (cIframe) {
|
|
997
998
|
cIframe.style.display = 'block';
|
|
998
|
-
// Lazy-load ComfyUI on first switch
|
|
999
|
+
// Lazy-load ComfyUI on first switch. URL is configurable via
|
|
1000
|
+
// window.COMFYUI_URL (set it from the dashboard or page) so users
|
|
1001
|
+
// on non-default ports or remote ComfyUI hosts can override.
|
|
999
1002
|
if (!cIframe.src || cIframe.src === 'about:blank' || cIframe.src === '') {
|
|
1000
|
-
cIframe.src = 'http://127.0.0.1:8188';
|
|
1003
|
+
cIframe.src = (typeof window !== 'undefined' && window.COMFYUI_URL) || 'http://127.0.0.1:8188';
|
|
1001
1004
|
}
|
|
1002
1005
|
}
|
|
1003
1006
|
if (dTab) { dTab.style.color = '#8892b0'; dTab.style.borderBottomColor = 'transparent'; }
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -567,11 +567,34 @@ function withFileLock(filePath, fn) {
|
|
|
567
567
|
try { return fn(); } finally { try { fs.unlinkSync(lockPath); } catch {} }
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
+
// Virtual operator agents. "Dashboard" and "Owner" represent the operator UI,
|
|
571
|
+
// not a CLI process. Surfacing them here ensures list_agents, broadcast filters,
|
|
572
|
+
// and DM-routing in the broker all agree the operator is a real recipient.
|
|
573
|
+
const VIRTUAL_AGENT_NAMES = ['Dashboard', 'Owner'];
|
|
574
|
+
function _mergeVirtualAgents(agents) {
|
|
575
|
+
const now = new Date().toISOString();
|
|
576
|
+
for (const name of VIRTUAL_AGENT_NAMES) {
|
|
577
|
+
if (!agents[name] || !agents[name].is_virtual) {
|
|
578
|
+
agents[name] = {
|
|
579
|
+
pid: -1,
|
|
580
|
+
is_virtual: true,
|
|
581
|
+
virtual_type: 'owner',
|
|
582
|
+
timestamp: (agents[name] && agents[name].timestamp) || now,
|
|
583
|
+
last_activity: now,
|
|
584
|
+
last_listened_at: now,
|
|
585
|
+
provider: 'Dashboard',
|
|
586
|
+
branch: (agents[name] && agents[name].branch) || 'main',
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return agents;
|
|
591
|
+
}
|
|
592
|
+
|
|
570
593
|
function getAgents() {
|
|
571
594
|
return cachedRead('agents', () => {
|
|
572
|
-
if (!fs.existsSync(AGENTS_FILE)) return {};
|
|
595
|
+
if (!fs.existsSync(AGENTS_FILE)) return _mergeVirtualAgents({});
|
|
573
596
|
let agents;
|
|
574
|
-
try { agents = JSON.parse(fs.readFileSync(AGENTS_FILE, 'utf8')); } catch { return {}; }
|
|
597
|
+
try { agents = JSON.parse(fs.readFileSync(AGENTS_FILE, 'utf8')); } catch { return _mergeVirtualAgents({}); }
|
|
575
598
|
// Scale fix: merge per-agent heartbeat files for live activity data
|
|
576
599
|
try {
|
|
577
600
|
const files = fs.readdirSync(DATA_DIR).filter(f => f.startsWith('heartbeat-') && f.endsWith('.json'));
|
|
@@ -586,7 +609,7 @@ function getAgents() {
|
|
|
586
609
|
}
|
|
587
610
|
}
|
|
588
611
|
} catch {}
|
|
589
|
-
return agents;
|
|
612
|
+
return _mergeVirtualAgents(agents);
|
|
590
613
|
}, 1500);
|
|
591
614
|
}
|
|
592
615
|
|
|
@@ -616,6 +639,10 @@ function getAcks(branch = currentBranch) {
|
|
|
616
639
|
// Cache for isPidAlive results — avoids redundant process.kill calls at 100-agent scale
|
|
617
640
|
const _pidAliveCache = {};
|
|
618
641
|
function isPidAlive(pid, lastActivity) {
|
|
642
|
+
// Virtual agents (Dashboard, Owner) use pid === -1 and are always alive.
|
|
643
|
+
// They represent the operator UI; liveness is implicit while the broker runs.
|
|
644
|
+
if (pid === -1) return true;
|
|
645
|
+
|
|
619
646
|
// Cache with 5s TTL — PID status doesn't change faster than heartbeats
|
|
620
647
|
const cacheKey = `${pid}_${lastActivity}`;
|
|
621
648
|
const cached = _pidAliveCache[cacheKey];
|
|
@@ -2765,7 +2792,11 @@ function toolBroadcast(content) {
|
|
|
2765
2792
|
if (sizeErr) return sizeErr;
|
|
2766
2793
|
|
|
2767
2794
|
const agents = getAgents();
|
|
2768
|
-
|
|
2795
|
+
// Exclude self and virtual agents (Dashboard, Owner) from broadcast recipients.
|
|
2796
|
+
// Virtual agents represent the operator UI and read from the shared message log
|
|
2797
|
+
// directly; they don't need per-recipient DM copies. Group-mode __group__ writes
|
|
2798
|
+
// are still visible to the UI via /api/history.
|
|
2799
|
+
const otherAgents = Object.keys(agents).filter(n => n !== registeredName && !agents[n].is_virtual);
|
|
2769
2800
|
|
|
2770
2801
|
if (otherAgents.length === 0) {
|
|
2771
2802
|
return { error: 'No other agents registered' };
|
|
@@ -8391,7 +8422,7 @@ function toolToggleRule(ruleId) {
|
|
|
8391
8422
|
// --- MCP Server setup ---
|
|
8392
8423
|
|
|
8393
8424
|
const server = new Server(
|
|
8394
|
-
{ name: 'agent-bridge', version: '5.4.
|
|
8425
|
+
{ name: 'agent-bridge', version: '5.4.1' },
|
|
8395
8426
|
{ capabilities: { tools: {} } }
|
|
8396
8427
|
);
|
|
8397
8428
|
|
|
@@ -9531,7 +9562,7 @@ async function main() {
|
|
|
9531
9562
|
try {
|
|
9532
9563
|
const transport = new StdioServerTransport();
|
|
9533
9564
|
await server.connect(transport);
|
|
9534
|
-
console.error('Agent Bridge MCP server v5.4.
|
|
9565
|
+
console.error('Agent Bridge MCP server v5.4.1 running (66 tools)');
|
|
9535
9566
|
} catch (e) {
|
|
9536
9567
|
console.error('ERROR: MCP server failed to start: ' + e.message);
|
|
9537
9568
|
console.error('Fix: Run "npx let-them-talk doctor" to check your setup.');
|