let-them-talk 3.3.1 → 3.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.3.2] - 2026-03-14
4
+
5
+ ### Changed
6
+ - License changed from MIT to Business Source License 1.1 (BSL)
7
+ - Added SECURITY.md with vulnerability disclosure policy
8
+ - Added CHANGELOG.md to published npm package
9
+ - Added .npmignore for cleaner package distribution
10
+ - Version synced across all files (server, CLI, dashboard)
11
+
3
12
  ## [3.0.0] - 2026-03-14
4
13
 
5
14
  ### Added — Agent Profiles
package/LICENSE CHANGED
@@ -1,21 +1,75 @@
1
- MIT License
2
-
3
- Copyright (c) 2024-2026 Dekelelz
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ Business Source License 1.1
2
+
3
+ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
4
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
5
+
6
+ Parameters
7
+
8
+ Licensor: Dekelelz
9
+ Licensed Work: Let Them Talk v3.3.3
10
+ The Licensed Work is (c) 2024-2026 Dekelelz.
11
+ Additional Use Grant: You may make use of the Licensed Work, provided that
12
+ you may not use the Licensed Work for a Commercial
13
+ Hosted Service. A "Commercial Hosted Service" is a
14
+ service offered to third parties on a hosted or
15
+ managed basis that provides substantially the same
16
+ functionality as the Licensed Work, or any modified
17
+ version thereof, as a commercial offering.
18
+
19
+ For clarity, the following uses are always permitted:
20
+ - Self-hosting for personal or internal business use
21
+ - Using as a development tool in any project
22
+ - Integrating into non-competing products
23
+ - Academic and research use
24
+ - Non-commercial use of any kind
25
+
26
+ Change Date: 2028-03-14
27
+ Change License: Apache License, Version 2.0
28
+
29
+ For information about alternative licensing arrangements for the Licensed Work,
30
+ please contact: contact@talk.unrealai.studio
31
+
32
+ Notice
33
+
34
+ Business Source License 1.1
35
+
36
+ Terms
37
+
38
+ The Licensor hereby grants you the right to copy, modify, create derivative
39
+ works, redistribute, and make non-production use of the Licensed Work. The
40
+ Licensor may make an Additional Use Grant, above, permitting limited production
41
+ use.
42
+
43
+ Effective on the Change Date, or the fourth anniversary of the first publicly
44
+ available distribution of a specific version of the Licensed Work under this
45
+ License, whichever comes first, the Licensor hereby grants you rights under the
46
+ terms of the Change License, and the rights granted in the paragraph above
47
+ terminate.
48
+
49
+ If your use of the Licensed Work does not comply with the requirements currently
50
+ in effect as described in this License, you must purchase a commercial license
51
+ from the Licensor, its affiliated entities, or authorized resellers, or you must
52
+ refrain from using the Licensed Work.
53
+
54
+ All copies of the original and modified Licensed Work, and derivative works of
55
+ the Licensed Work, are subject to this License. This License applies separately
56
+ for each version of the Licensed Work and the Change Date may vary for each
57
+ version of the Licensed Work released by Licensor.
58
+
59
+ You must conspicuously display this License on each original or modified copy of
60
+ the Licensed Work. If you receive the Licensed Work in original or modified form
61
+ from a third party, the terms and conditions set forth in this License apply to
62
+ your use of that work.
63
+
64
+ Any use of the Licensed Work in violation of this License will automatically
65
+ terminate your rights under this License for the current and all other versions
66
+ of the Licensed Work.
67
+
68
+ This License does not grant you any right in any trademark or logo of Licensor
69
+ or its affiliates (provided that you may use a trademark or logo of Licensor as
70
+ expressly required by this License).
71
+
72
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
73
+ "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS
74
+ OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY,
75
+ FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
package/README.md CHANGED
@@ -1,63 +1,148 @@
1
- # Let Them Talk
1
+ <p align="center">
2
+ <img src="agent-bridge/logo.png" alt="Let Them Talk" width="120">
3
+ </p>
2
4
 
3
- [![npm version](https://img.shields.io/npm/v/let-them-talk.svg)](https://www.npmjs.com/package/let-them-talk)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
- [![Discord](https://img.shields.io/discord/1482478651000885359?color=5865F2&label=Discord&logo=discord&logoColor=white)](https://discord.gg/6Y9YgkFNJP)
5
+ <h1 align="center">Let Them Talk</h1>
6
6
 
7
- **MCP server + web dashboard that lets AI CLI agents talk to each other.**
7
+ <p align="center">
8
+ <strong>Multi-agent collaboration for AI CLI terminals.</strong><br>
9
+ Let your AI agents talk, delegate, review, and build together.
10
+ </p>
8
11
 
9
- Open two (or more) Claude Code, Gemini CLI, or Codex CLI terminals — and let them collaborate, debate, review code, or divide tasks. Watch the conversation unfold in a real-time web dashboard with a kanban board, agent monitoring, and message injection.
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"></a>
14
+ <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>
15
+ <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>
16
+ <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="Downloads"></a>
17
+ </p>
10
18
 
11
- ## Quick Start
19
+ <p align="center">
20
+ <a href="https://talk.unrealai.studio">Website</a> ·
21
+ <a href="#quick-start">Quick Start</a> ·
22
+ <a href="VISION.md">Vision</a> ·
23
+ <a href="#agent-templates">Templates</a> ·
24
+ <a href="#web-dashboard">Dashboard</a> ·
25
+ <a href="https://discord.gg/6Y9YgkFNJP">Discord</a>
26
+ </p>
12
27
 
13
- ```bash
14
- # 1. Install in any project
15
- npx let-them-talk init
28
+ ---
16
29
 
17
- # 2. Launch the web dashboard
18
- npx let-them-talk dashboard
30
+ Let Them Talk is an MCP server that connects multiple AI CLI terminals through a shared filesystem. Open Claude Code, Gemini CLI, or Codex CLI in separate terminals — they discover each other, exchange messages, share files, assign tasks, and coordinate through workflows. A real-time web dashboard lets you watch everything unfold, inject messages, and manage the conversation.
19
31
 
20
- # 3. In Terminal 1: tell the agent to register as "A", say hello, then call listen()
21
- # 4. In Terminal 2: tell the agent to register as "B", then call listen()
22
- ```
32
+ If you want your AI agents to stop working in isolation and start collaborating like a team, this is it.
33
+
34
+ ## Quick Start
23
35
 
24
- Or use a template for guided setup:
36
+ Preferred setup: one command to install, one to launch the dashboard.
25
37
 
26
38
  ```bash
27
- npx let-them-talk init --template team # Coordinator + Researcher + Coder
28
- npx let-them-talk init --template review # Author + Reviewer
29
- npx let-them-talk init --template debate # Pro + Con
30
- npx let-them-talk templates # List all templates
39
+ npx let-them-talk init # auto-detects your CLI and configures MCP
40
+ npx let-them-talk dashboard # opens the web dashboard at localhost:3000
31
41
  ```
32
42
 
43
+ Then open two terminals and tell each agent to register:
44
+
45
+ **Terminal 1:** `Register as "A", say hello to B, then call listen()`
46
+
47
+ **Terminal 2:** `Register as "B", then call listen()`
48
+
49
+ That's it. They'll start talking. Watch it live in the dashboard.
50
+
51
+ > **Templates:** Skip the manual setup with `npx let-them-talk init --template team` — gives you ready-to-paste prompts for a Coordinator + Researcher + Coder team. [See all templates](#agent-templates).
52
+
53
+ ## Supported CLIs
54
+
55
+ | CLI | Config File | Auto-detected |
56
+ |-----|-------------|:-------------:|
57
+ | Claude Code | `.mcp.json` | Yes |
58
+ | Gemini CLI | `.gemini/settings.json` | Yes |
59
+ | Codex CLI | `.codex/config.toml` | Yes |
60
+
61
+ Run `npx let-them-talk init --all` to configure all three at once.
62
+
33
63
  ## How It Works
34
64
 
35
65
  ```
36
- Terminal 1 (Claude Code) Terminal 2 (Gemini CLI) Terminal 3 (Codex CLI)
37
- | | |
38
- v v v
39
- MCP Server MCP Server MCP Server
40
- (stdio process) (stdio process) (stdio process)
41
- | | |
42
- +------------- Shared Filesystem (.agent-bridge/) ----------------+
43
- | messages.jsonl | history.jsonl |
44
- | agents.json | tasks.json |
45
- | profiles.json | workflows.json |
46
- | workspaces/ | plugins/ |
47
- |
48
- v
49
- Web Dashboard (localhost:3000)
50
- Real-time SSE + Agent monitoring
51
- Tasks + Workspaces + Workflows + Plugins
66
+ Terminal 1 Terminal 2 Terminal 3
67
+ (Claude Code) (Gemini CLI) (Codex CLI)
68
+ | | |
69
+ v v v
70
+ MCP Server MCP Server MCP Server
71
+ (stdio) (stdio) (stdio)
72
+ | | |
73
+ +----------- .agent-bridge/ directory ----------+
74
+ messages · agents · tasks
75
+ profiles · workflows · plugins
76
+ |
77
+ v
78
+ Web Dashboard :3000
79
+ SSE real-time · Kanban
80
+ Agent monitoring · Injection
81
+ ```
82
+
83
+ Each terminal spawns its own MCP server process. All processes share a `.agent-bridge/` directory in your project root. The dashboard reads the same files via Server-Sent Events for instant updates.
84
+
85
+ ## Highlights
86
+
87
+ - **27 MCP tools** — messaging, tasks, workflows, profiles, workspaces, branching, plugins
88
+ - **Real-time dashboard** — SSE-powered (~200ms latency), markdown rendering, dark/light theme
89
+ - **Multi-agent** — 2 agents auto-route, 3+ agents specify recipients, broadcast to all
90
+ - **Task management** — kanban board with create/assign/track between agents
91
+ - **Workflow pipelines** — multi-step automation with auto-handoff
92
+ - **Agent profiles** — display names, SVG avatars, roles, bios
93
+ - **Conversation branching** — fork at any point, isolated history per branch
94
+ - **File sharing** — send code, diffs, and results directly between agents
95
+ - **Plugin system** — extend with custom tools, 30s sandboxed execution
96
+ - **Zero config** — one `npx` command, auto-detects your CLI, works immediately
97
+
98
+ ## Agent Templates
99
+
100
+ Pre-built team configurations. Each template gives you ready-to-paste prompts for every terminal.
101
+
102
+ ```bash
103
+ npx let-them-talk init --template pair # A + B
104
+ npx let-them-talk init --template team # Coordinator + Researcher + Coder
105
+ npx let-them-talk init --template review # Author + Reviewer
106
+ npx let-them-talk init --template debate # Pro + Con
107
+ npx let-them-talk templates # List all available templates
52
108
  ```
53
109
 
54
- Each CLI terminal spawns its own MCP server process via stdio. All processes read/write to a shared `.agent-bridge/` directory. The dashboard monitors the same files via Server-Sent Events for real-time updates.
110
+ | Template | Agents | Best For |
111
+ |----------|--------|----------|
112
+ | **pair** | A, B | Brainstorming, Q&A, simple conversations |
113
+ | **team** | Coordinator, Researcher, Coder | Complex features needing research + implementation |
114
+ | **review** | Author, Reviewer | Code review with structured feedback loops |
115
+ | **debate** | Pro, Con | Evaluating trade-offs, architecture decisions |
116
+
117
+ ## Web Dashboard
118
+
119
+ Launch with `npx let-them-talk dashboard` — opens at `http://localhost:3000`.
120
+
121
+ **4 main tabs:**
55
122
 
56
- ## Features
123
+ - **Messages** — live feed with full markdown, message grouping, search, bookmarks, pins, emoji reactions, conversation replay
124
+ - **Tasks** — kanban board (pending / in progress / done / blocked), update status from dashboard
125
+ - **Workspaces** — per-agent key-value storage browser
126
+ - **Workflows** — horizontal pipeline visualization, advance or skip steps
57
127
 
58
- ### 27 MCP Tools + Plugins
128
+ **Plus:**
59
129
 
60
- **Messaging**
130
+ - Agent monitoring with active / sleeping / dead / listening status
131
+ - Profile popups with avatars and role badges
132
+ - Activity heatmap and per-agent stats
133
+ - Message injection and broadcast from browser
134
+ - Conversation branching with branch tabs
135
+ - Export as shareable HTML or Markdown
136
+ - Multi-project support with auto-discover
137
+ - Dark / light theme toggle
138
+ - Mobile responsive with hamburger sidebar
139
+ - Browser notifications and sound alerts
140
+ - LAN mode for phone access
141
+
142
+ ## MCP Tools (27 + plugins)
143
+
144
+ <details>
145
+ <summary><strong>Messaging (13 tools)</strong></summary>
61
146
 
62
147
  | Tool | Description |
63
148
  |------|-------------|
@@ -71,112 +156,54 @@ Each CLI terminal spawns its own MCP server process via stdio. All processes rea
71
156
  | `ack_message` | Confirm message was processed |
72
157
  | `get_history` | View conversation with thread/branch filter |
73
158
  | `get_summary` | Condensed conversation recap |
74
- | `handoff` | Transfer work to another agent with context |
75
- | `share_file` | Send file contents to another agent |
76
- | `reset` | Clear data (auto-archives first) |
159
+ | `handoff` | Transfer work with context |
160
+ | `share_file` | Send file contents (max 100KB) |
161
+ | `reset` | Clear all data (auto-archives first) |
162
+
163
+ </details>
77
164
 
78
- **Tasks & Workflows**
165
+ <details>
166
+ <summary><strong>Tasks & Workflows (6 tools)</strong></summary>
79
167
 
80
168
  | Tool | Description |
81
169
  |------|-------------|
82
170
  | `create_task` | Create and assign tasks |
83
- | `update_task` | Update task status (pending/in_progress/done/blocked) |
171
+ | `update_task` | Update status: pending / in_progress / done / blocked |
84
172
  | `list_tasks` | View tasks with filters |
85
173
  | `create_workflow` | Create multi-step pipeline with assignees |
86
174
  | `advance_workflow` | Complete current step, auto-handoff to next |
87
- | `workflow_status` | Get workflow progress |
175
+ | `workflow_status` | Get workflow progress percentage |
88
176
 
89
- **Profiles & Workspaces**
177
+ </details>
178
+
179
+ <details>
180
+ <summary><strong>Profiles & Workspaces (4 tools)</strong></summary>
90
181
 
91
182
  | Tool | Description |
92
183
  |------|-------------|
93
184
  | `update_profile` | Set display name, avatar, bio, role |
94
- | `workspace_write` | Write to your key-value workspace (50 keys, 100KB/value) |
95
- | `workspace_read` | Read workspace entries (yours or another agent's) |
185
+ | `workspace_write` | Write key-value data (50 keys, 100KB/value) |
186
+ | `workspace_read` | Read your workspace or another agent's |
96
187
  | `workspace_list` | List workspace keys |
97
188
 
98
- **Conversation Branching**
189
+ </details>
190
+
191
+ <details>
192
+ <summary><strong>Branching (3 tools)</strong></summary>
99
193
 
100
194
  | Tool | Description |
101
195
  |------|-------------|
102
- | `fork_conversation` | Fork conversation at any message point |
196
+ | `fork_conversation` | Fork at any message point |
103
197
  | `switch_branch` | Switch to a different branch |
104
198
  | `list_branches` | List all branches with message counts |
105
199
 
106
- ### Web Dashboard (4 tabs)
107
-
108
- - **Messages** — SSE-powered real-time feed, full markdown, message grouping, date separators, bookmarks, pins, emoji reactions, search, conversation replay
109
- - **Tasks** — Kanban board (pending/in_progress/done/blocked), status updates from dashboard
110
- - **Workspaces** — Per-agent key-value browser with collapsible accordion UI
111
- - **Workflows** — Horizontal pipeline visualization, advance/skip steps from dashboard
112
- - **Agent monitoring** — active/sleeping/dead/listening status, profile popups with avatars, provider badges, activity heatmap
113
- - **Conversation branching** — branch tabs, switch between conversation forks
114
- - **Message injection** — send messages or broadcast to agents from the browser
115
- - **Plugin management** — plugin cards with enable/disable toggles
116
- - **Export** — shareable HTML or Markdown download
117
- - **Multi-project** — monitor multiple folders + auto-discover
118
- - **Dark/light theme** — toggle with localStorage persistence
119
- - **Mobile responsive** — hamburger sidebar, works on phones and tablets
120
-
121
- ### Reliability
122
-
123
- - **Heartbeat** — 10s pings track agent liveness
124
- - **Auto-compact** — message queue cleaned when > 500 lines
125
- - **Auto-archive** — conversations saved before reset
126
- - **Context hints** — warns agents when conversation gets long
127
- - **Dead recipient warnings** — alerts when sending to offline agents
128
- - **Clean exit** — agents deregister on process exit
129
-
130
- ## Agent Templates
131
-
132
- Pre-built team configurations with ready-to-paste prompts:
133
-
134
- | Template | Agents | Best For |
135
- |----------|--------|----------|
136
- | `pair` | A, B | Simple conversations, brainstorming |
137
- | `team` | Coordinator, Researcher, Coder | Complex features, research + implementation |
138
- | `review` | Author, Reviewer | Code review with structured feedback |
139
- | `debate` | Pro, Con | Evaluating trade-offs and decisions |
140
-
141
- ## CLI Commands
142
-
143
- ```bash
144
- npx let-them-talk init # Auto-detect CLI and configure
145
- npx let-them-talk init --all # Configure for all CLIs
146
- npx let-them-talk init --template <name> # Use a team template
147
- npx let-them-talk templates # List available templates
148
- npx let-them-talk dashboard # Launch web dashboard
149
- npx let-them-talk reset # Clear conversation data
150
- npx let-them-talk plugin list # List installed plugins
151
- npx let-them-talk plugin add <file.js> # Install a plugin
152
- npx let-them-talk plugin remove <name> # Remove a plugin
153
- npx let-them-talk plugin enable <name> # Enable a plugin
154
- npx let-them-talk plugin disable <name> # Disable a plugin
155
- npx let-them-talk help # Show help
156
- ```
157
-
158
- ## Updating
159
-
160
- ```bash
161
- # If using npx (recommended) — clear cache to get latest version
162
- npx clear-npx-cache
163
- npx let-them-talk init # Re-run to update MCP config paths
164
-
165
- # If installed globally
166
- npm update -g let-them-talk
167
-
168
- # Check your version
169
- npx let-them-talk help # Shows version in header
170
- ```
171
-
172
- After updating, restart your CLI terminals to pick up the new MCP server.
200
+ </details>
173
201
 
174
202
  ## Plugins
175
203
 
176
- Extend Let Them Talk with custom tools. Plugins are `.js` files in the `.agent-bridge/plugins/` directory.
204
+ Extend Let Them Talk with custom tools. Drop a `.js` file in `.agent-bridge/plugins/`.
177
205
 
178
206
  ```javascript
179
- // plugins/my-tool.js
180
207
  module.exports = {
181
208
  name: 'my-tool',
182
209
  description: 'What this tool does',
@@ -188,21 +215,56 @@ module.exports = {
188
215
  required: ['query']
189
216
  },
190
217
  handler(args, ctx) {
191
- // ctx provides: sendMessage, getAgents, getHistory, readFile, registeredName, dataDir
218
+ // ctx: sendMessage, getAgents, getHistory, readFile, registeredName, dataDir
192
219
  return { result: 'done', query: args.query };
193
220
  }
194
221
  };
195
222
  ```
196
223
 
197
- Plugins run sandboxed with a 30-second timeout. Manage them via CLI or the dashboard.
224
+ ```bash
225
+ npx let-them-talk plugin add my-tool.js # install
226
+ npx let-them-talk plugin list # list installed
227
+ npx let-them-talk plugin remove my-tool # remove
228
+ npx let-them-talk plugin enable my-tool # enable
229
+ npx let-them-talk plugin disable my-tool # disable
230
+ ```
198
231
 
199
- ## Supported CLIs
232
+ Plugins run sandboxed with a 30-second timeout. Manage via CLI or dashboard.
200
233
 
201
- | CLI | Config | Auto-detected |
202
- |-----|--------|---------------|
203
- | Claude Code | `.mcp.json` | Yes |
204
- | Gemini CLI | `.gemini/settings.json` | Yes |
205
- | Codex CLI | `.mcp.json` | Yes |
234
+ ## CLI Reference
235
+
236
+ ```bash
237
+ npx let-them-talk init # auto-detect CLI, configure MCP
238
+ npx let-them-talk init --all # configure all CLIs
239
+ npx let-them-talk init --template <name> # use a team template
240
+ npx let-them-talk templates # list templates
241
+ npx let-them-talk dashboard # launch web dashboard
242
+ npx let-them-talk reset # clear conversation data
243
+ npx let-them-talk plugin <subcommand> # manage plugins
244
+ npx let-them-talk help # show help
245
+ ```
246
+
247
+ ## Updating
248
+
249
+ ```bash
250
+ npx clear-npx-cache # clear cached version
251
+ npx let-them-talk init # re-run to update config
252
+ npx let-them-talk help # verify version
253
+ ```
254
+
255
+ After updating, restart your CLI terminals to pick up the new MCP server.
256
+
257
+ ## Security
258
+
259
+ Let Them Talk is a **local message broker**. It passes text messages between CLI terminals via shared files on your machine. It does **not** give agents any capabilities beyond what they already have.
260
+
261
+ **Does not:** access the internet, store API keys, run cloud services, or grant new filesystem access.
262
+
263
+ **Built-in protections:** CORS restriction, XSS prevention, path traversal protection, symlink validation, origin enforcement, SSE connection limits, input validation, message size limits (1MB), plugin sandboxing (30s timeout).
264
+
265
+ **LAN mode:** Optional phone access exposes the dashboard to your local WiFi only. Requires explicit activation.
266
+
267
+ Full details: [SECURITY.md](SECURITY.md)
206
268
 
207
269
  ## Environment Variables
208
270
 
@@ -210,34 +272,27 @@ Plugins run sandboxed with a 30-second timeout. Manage them via CLI or the dashb
210
272
  |----------|---------|-------------|
211
273
  | `AGENT_BRIDGE_DATA_DIR` | `{cwd}/.agent-bridge/` | Data directory path |
212
274
  | `AGENT_BRIDGE_PORT` | `3000` | Dashboard port |
275
+ | `AGENT_BRIDGE_LAN` | `false` | Enable LAN mode |
213
276
  | `NODE_ENV` | — | Set to `development` for hot-reload |
214
277
 
215
- ## Security
278
+ ## Contributing
216
279
 
217
- Let Them Talk is a **local message broker** — it passes text messages between CLI terminals via shared files. It does **not** give agents any new capabilities beyond what they already have.
280
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
218
281
 
219
- ### What it does NOT do
220
- - Does not give agents filesystem access (they already have it via their CLI)
221
- - Does not expose anything to the internet (dashboard binds to localhost only)
222
- - Does not store or transmit API keys
223
- - Does not run any cloud services
282
+ ## Contact
224
283
 
225
- ### Built-in protections
226
- - **CSRF protection** — external websites cannot send requests to the dashboard
227
- - **XSS prevention** — all inputs are escaped before rendering
228
- - **Path traversal protection** — agents cannot read files outside the project directory
229
- - **Symlink protection** — follows symlinks and validates the real path
230
- - **Origin enforcement** — POST/DELETE requests require valid localhost/LAN origin
231
- - **SSE connection limits** — prevents connection exhaustion DoS
232
- - **Forced sender identity** — dashboard messages are always marked as "Dashboard"
233
- - **Input validation** — branch names, agent names, and paths are validated
284
+ For business inquiries, licensing, and partnerships: **contact@talk.unrealai.studio**
234
285
 
235
- ### LAN mode
236
- LAN mode (phone access) only exposes the dashboard to your local WiFi network, not the internet. It requires explicit activation and a firewall rule. A warning is shown when enabled.
286
+ ## License
237
287
 
238
- ### Plugins
239
- Plugins run with full Node.js access. Only install plugins you trust. This is the same trust model as npm packages.
288
+ [Business Source License 1.1](LICENSE) — Free to use, self-host, and modify. Cannot be offered as a competing commercial hosted service. Converts to Apache 2.0 on March 14, 2028.
240
289
 
241
- ## License
290
+ ---
242
291
 
243
- MIT
292
+ <p align="center">
293
+ Built by <a href="https://github.com/Dekelelz">Dekelelz</a> ·
294
+ <a href="https://talk.unrealai.studio">Website</a> ·
295
+ <a href="https://discord.gg/6Y9YgkFNJP">Discord</a> ·
296
+ <a href="https://www.npmjs.com/package/let-them-talk">npm</a> ·
297
+ <a href="mailto:contact@talk.unrealai.studio">Contact</a>
298
+ </p>
package/SECURITY.md CHANGED
@@ -14,7 +14,7 @@ If you discover a security vulnerability in Let Them Talk, please report it resp
14
14
 
15
15
  **Do NOT open a public GitHub issue for security vulnerabilities.**
16
16
 
17
- Instead, please email **security@dos-technology.com** or use [GitHub's private vulnerability reporting](https://github.com/Dekelelz/let-them-talk/security/advisories/new).
17
+ Instead, please email **contact@talk.unrealai.studio** or use [GitHub's private vulnerability reporting](https://github.com/Dekelelz/let-them-talk/security/advisories/new).
18
18
 
19
19
  ### What to include
20
20
 
package/cli.js CHANGED
@@ -8,7 +8,7 @@ const command = process.argv[2];
8
8
 
9
9
  function printUsage() {
10
10
  console.log(`
11
- Let Them Talk — Agent Bridge v3.3.0
11
+ Let Them Talk — Agent Bridge v3.3.3
12
12
  MCP message broker for inter-agent communication
13
13
  Supports: Claude Code, Gemini CLI, Codex CLI
14
14
 
package/dashboard.html CHANGED
@@ -1737,9 +1737,11 @@
1737
1737
  }
1738
1738
 
1739
1739
  /* ===== RESPONSIVE ===== */
1740
+ /* ===== MOBILE: TABLET (768px) ===== */
1740
1741
  @media (max-width: 768px) {
1741
1742
  :root {
1742
- --sidebar-w: 260px;
1743
+ --sidebar-w: 280px;
1744
+ --header-h: 50px;
1743
1745
  }
1744
1746
 
1745
1747
  .mobile-toggle { display: block; }
@@ -1752,6 +1754,7 @@
1752
1754
  z-index: 90;
1753
1755
  transform: translateX(-100%);
1754
1756
  box-shadow: 4px 0 20px rgba(0,0,0,0.3);
1757
+ transition: transform 0.25s ease;
1755
1758
  }
1756
1759
 
1757
1760
  .sidebar.open { transform: translateX(0); }
@@ -1769,19 +1772,143 @@
1769
1772
 
1770
1773
  .header-stats { display: none; }
1771
1774
 
1775
+ /* Header: compact layout */
1776
+ .header { padding: 0 10px; gap: 6px; }
1777
+ .header-left { gap: 8px; }
1778
+ .header-actions { gap: 4px; }
1779
+
1780
+ /* Hide non-essential header buttons on mobile */
1781
+ .header-actions .connection { display: none; }
1782
+ .header-actions .btn { display: none; }
1783
+ .header-actions .phone-btn,
1784
+ .header-actions .theme-toggle,
1785
+ .header-actions .notif-toggle { display: inline-flex; }
1786
+ .header-actions .sound-toggle { display: none; }
1787
+
1788
+ /* Show mobile project selector in header */
1789
+ #mobile-project-name { display: none !important; }
1790
+ .mobile-project-select {
1791
+ display: inline-block !important;
1792
+ background: var(--surface-2);
1793
+ color: var(--orange);
1794
+ border: 1px solid var(--orange-dim);
1795
+ border-radius: 8px;
1796
+ padding: 3px 6px;
1797
+ font-size: 11px;
1798
+ font-weight: 600;
1799
+ max-width: 140px;
1800
+ cursor: pointer;
1801
+ outline: none;
1802
+ }
1803
+ .mobile-project-select:focus { border-color: var(--orange); }
1804
+
1805
+ /* Messages: better mobile spacing */
1806
+ .messages-area { padding: 8px; gap: 1px; }
1807
+ .message { padding: 8px 10px; gap: 8px; }
1808
+ .msg-avatar { width: 28px; height: 28px; font-size: 11px; }
1809
+ .msg-from { font-size: 12px; }
1810
+ .msg-content { font-size: 13px; line-height: 1.5; }
1811
+ .msg-time { font-size: 9px; }
1812
+
1813
+ /* Message actions: always visible on mobile (no hover) */
1814
+ .message .msg-actions { display: flex; opacity: 0.4; }
1815
+ .message .msg-actions .msg-action-btn { opacity: 1; padding: 4px 6px; font-size: 14px; }
1816
+
1817
+ /* Input bar: stack vertically */
1772
1818
  .msg-input-bar {
1773
- flex-wrap: wrap;
1819
+ flex-direction: column;
1820
+ padding: 8px 10px;
1821
+ gap: 6px;
1774
1822
  }
1823
+ .input-target {
1824
+ flex-direction: row;
1825
+ align-items: center;
1826
+ min-width: unset;
1827
+ }
1828
+ .input-target label { display: none; }
1829
+ .input-target select { flex: 1; padding: 8px 10px; font-size: 13px; }
1830
+ .input-msg label { display: none; }
1831
+ .input-msg textarea { padding: 10px; font-size: 14px; min-height: 40px; }
1832
+ .send-btn { padding: 10px 16px; font-size: 13px; align-self: flex-end; }
1833
+
1834
+ /* View tabs: scrollable on mobile */
1835
+ .view-tabs {
1836
+ overflow-x: auto;
1837
+ -webkit-overflow-scrolling: touch;
1838
+ scrollbar-width: none;
1839
+ flex-wrap: nowrap;
1840
+ }
1841
+ .view-tabs::-webkit-scrollbar { display: none; }
1842
+ .view-tab { white-space: nowrap; flex-shrink: 0; flex: none; padding: 8px 14px; font-size: 12px; }
1843
+
1844
+ /* Search bar */
1845
+ .search-input { font-size: 14px; padding: 8px 10px; }
1775
1846
 
1776
- .input-target { min-width: 80px; }
1847
+ /* Phone modal: full-width on mobile */
1848
+ .phone-modal { width: 95vw; max-width: 95vw; }
1777
1849
 
1778
- .messages-area { padding: 12px; }
1850
+ /* Scroll-to-bottom button: bigger touch target */
1851
+ .scroll-bottom { width: 44px; height: 44px; font-size: 18px; bottom: 80px; }
1779
1852
  }
1780
1853
 
1854
+ /* ===== MOBILE: PHONE (480px) ===== */
1781
1855
  @media (max-width: 480px) {
1782
- .header { padding: 0 12px; }
1783
- .logo { font-size: 15px; }
1784
- .msg-input-bar { padding: 10px 12px; }
1856
+ :root {
1857
+ --header-h: 46px;
1858
+ }
1859
+
1860
+ .header { padding: 0 8px; }
1861
+ .logo { font-size: 14px; }
1862
+ .header-left { gap: 6px; }
1863
+
1864
+ /* Compact header buttons */
1865
+ .header-actions { gap: 2px; }
1866
+ .phone-btn { padding: 4px 6px; font-size: 13px; }
1867
+ .theme-toggle, .notif-toggle { font-size: 16px; padding: 4px; }
1868
+
1869
+ /* Mobile project select: compact */
1870
+ .mobile-project-select { font-size: 10px; padding: 2px 4px; max-width: 110px; }
1871
+
1872
+ /* Messages: tighter for small screens */
1873
+ .messages-area { padding: 6px; }
1874
+ .message { padding: 6px 8px; gap: 6px; border-radius: 6px; }
1875
+ .msg-avatar { width: 24px; height: 24px; font-size: 10px; }
1876
+ .msg-header { gap: 4px; }
1877
+ .msg-from { font-size: 11px; }
1878
+ .msg-to { font-size: 11px; }
1879
+ .msg-content { font-size: 12px; }
1880
+ .msg-content pre { font-size: 11px; padding: 8px; }
1881
+ .msg-content code { font-size: 11px; }
1882
+
1883
+ /* Reactions: smaller */
1884
+ .reaction-chip { font-size: 11px; padding: 1px 5px; }
1885
+
1886
+ /* Input bar: full-width, compact */
1887
+ .msg-input-bar { padding: 6px 8px; gap: 4px; }
1888
+ .input-target select { padding: 7px 8px; font-size: 12px; }
1889
+ .input-msg textarea { padding: 8px; font-size: 13px; }
1890
+ .send-btn { padding: 8px 14px; font-size: 12px; }
1891
+
1892
+ /* Sidebar: full-width on very small phones */
1893
+ .sidebar { width: 100vw; }
1894
+
1895
+ /* Thread items */
1896
+ .thread-item { padding: 6px 8px; font-size: 11px; }
1897
+
1898
+ /* Pinned messages */
1899
+ .pinned-section .message { padding: 4px 10px; }
1900
+ .pinned-section .msg-content { font-size: 11px; }
1901
+
1902
+ /* View tabs */
1903
+ .view-tab { padding: 7px 12px; font-size: 11px; }
1904
+ }
1905
+
1906
+ /* ===== MOBILE: VERY SMALL PHONE (360px) ===== */
1907
+ @media (max-width: 360px) {
1908
+ .logo { font-size: 13px; }
1909
+ .mobile-project-select { max-width: 80px; font-size: 9px; }
1910
+ .msg-avatar { display: none; }
1911
+ .message { gap: 0; }
1785
1912
  }
1786
1913
 
1787
1914
  /* ===== v3.0: PROFILE AVATAR ===== */
@@ -2308,6 +2435,10 @@
2308
2435
  <button class="mobile-toggle" onclick="toggleSidebar()" aria-label="Menu">&#9776;</button>
2309
2436
  <img src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><rect rx='20' width='100' height='100' fill='%230d1117'/><path d='M20 30 Q20 20 30 20 H70 Q80 20 80 30 V55 Q80 65 70 65 H55 L40 80 V65 H30 Q20 65 20 55Z' fill='%2358a6ff'/><circle cx='38' cy='42' r='5' fill='%230d1117'/><circle cx='55' cy='42' r='5' fill='%230d1117'/></svg>" alt="" style="height:28px;margin-right:4px;vertical-align:middle">
2310
2437
  <div class="logo">Let Them Talk</div>
2438
+ <span id="mobile-project-name" style="display:none;font-size:11px;color:var(--orange);font-weight:600;background:var(--orange-dim);padding:2px 8px;border-radius:10px" onclick="toggleSidebar()"></span>
2439
+ <select id="mobile-project-select" class="mobile-project-select" onchange="mobileSelectProject(this.value)" style="display:none">
2440
+ <option value="">Select project...</option>
2441
+ </select>
2311
2442
  <div class="header-stats">
2312
2443
  <div class="h-stat"><span class="h-stat-val" id="stat-messages">0</span> msgs</div>
2313
2444
  <div class="h-stat"><span class="h-stat-val" id="stat-agents">0</span> agents</div>
@@ -2471,7 +2602,7 @@
2471
2602
  </div>
2472
2603
  </div>
2473
2604
  <div class="app-footer">
2474
- <span>Let Them Talk v3.3.0</span>
2605
+ <span>Let Them Talk v3.3.3</span>
2475
2606
  </div>
2476
2607
  <div class="profile-popup" id="profile-popup" onclick="event.stopPropagation()">
2477
2608
  <div class="profile-popup-header">
@@ -2530,6 +2661,8 @@ var activeThread = null;
2530
2661
  var activeProject = ''; // empty = default/local
2531
2662
  var cachedHistory = [];
2532
2663
  var cachedAgents = {};
2664
+ var isMobile = window.innerWidth <= 768 || /Mobi|Android|iPhone|iPad/i.test(navigator.userAgent);
2665
+ window.addEventListener('resize', function() { isMobile = window.innerWidth <= 768; });
2533
2666
 
2534
2667
  // Agent color palette
2535
2668
  var COLORS = [
@@ -2969,20 +3102,32 @@ function renderThreads(messages) {
2969
3102
  function renderMessages(messages) {
2970
3103
  var el = document.getElementById('messages');
2971
3104
 
3105
+
2972
3106
  if (!messages.length) {
2973
- el.innerHTML = '<div class="empty-state">' +
2974
- '<div class="empty-icon">&#x1f4ac;</div>' +
2975
- '<div class="empty-text">No messages yet</div>' +
2976
- '<div class="empty-sub">Get two AI agents talking in minutes</div>' +
2977
- '<div class="onboard-steps">' +
2978
- '<div class="onboard-step"><span class="onboard-num">1</span><span>Open two terminals and run <code>claude</code> in each</span></div>' +
2979
- '<div class="onboard-step"><span class="onboard-num">2</span><span>Paste this in Terminal 1:</span></div>' +
2980
- '<div class="copy-block" onclick="copyText(this)" data-text="You are Agent A. Register as &quot;A&quot;. Say hello to Agent B, then call listen()."><span class="copy-hint">click to copy</span>You are Agent A. Register as "A". Say hello to Agent B, then call listen().</div>' +
2981
- '<div class="onboard-step"><span class="onboard-num">3</span><span>Paste this in Terminal 2:</span></div>' +
2982
- '<div class="copy-block" onclick="copyText(this)" data-text="You are Agent B. Register as &quot;B&quot;. Call listen() to wait for messages. When you receive one, respond, then listen() again."><span class="copy-hint">click to copy</span>You are Agent B. Register as "B". Call listen() to wait for messages. When you receive one, respond, then listen() again.</div>' +
2983
- '<div class="onboard-step"><span class="onboard-num">4</span><span>Watch them talk here in real time!</span></div>' +
2984
- '</div>' +
2985
- '</div>';
3107
+ if (isMobile) {
3108
+ var mobileHint = !activeProject
3109
+ ? '<div class="empty-sub">Select a project above to see agent conversations</div>'
3110
+ : '<div class="empty-sub">Waiting for messages in: ' + activeProject.split(/[/\\]/).pop() + '</div>';
3111
+ el.innerHTML = '<div class="empty-state">' +
3112
+ '<div class="empty-icon">&#x1f4ac;</div>' +
3113
+ '<div class="empty-text">No messages yet</div>' +
3114
+ mobileHint +
3115
+ '</div>';
3116
+ } else {
3117
+ el.innerHTML = '<div class="empty-state">' +
3118
+ '<div class="empty-icon">&#x1f4ac;</div>' +
3119
+ '<div class="empty-text">No messages yet</div>' +
3120
+ '<div class="empty-sub">Get two AI agents talking in minutes</div>' +
3121
+ '<div class="onboard-steps">' +
3122
+ '<div class="onboard-step"><span class="onboard-num">1</span><span>Open two terminals and run <code>claude</code> in each</span></div>' +
3123
+ '<div class="onboard-step"><span class="onboard-num">2</span><span>Paste this in Terminal 1:</span></div>' +
3124
+ '<div class="copy-block" onclick="copyText(this)" data-text="You are Agent A. Register as &quot;A&quot;. Say hello to Agent B, then call listen()."><span class="copy-hint">click to copy</span>You are Agent A. Register as "A". Say hello to Agent B, then call listen().</div>' +
3125
+ '<div class="onboard-step"><span class="onboard-num">3</span><span>Paste this in Terminal 2:</span></div>' +
3126
+ '<div class="copy-block" onclick="copyText(this)" data-text="You are Agent B. Register as &quot;B&quot;. Call listen() to wait for messages. When you receive one, respond, then listen() again."><span class="copy-hint">click to copy</span>You are Agent B. Register as "B". Call listen() to wait for messages. When you receive one, respond, then listen() again.</div>' +
3127
+ '<div class="onboard-step"><span class="onboard-num">4</span><span>Watch them talk here in real time!</span></div>' +
3128
+ '</div>' +
3129
+ '</div>';
3130
+ }
2986
3131
  return;
2987
3132
  }
2988
3133
 
@@ -3141,6 +3286,7 @@ function filterThread(tid) {
3141
3286
  lastMessageCount = 0;
3142
3287
  renderThreads(cachedHistory);
3143
3288
  renderMessages(cachedHistory);
3289
+ if (isMobile) closeSidebar();
3144
3290
  }
3145
3291
 
3146
3292
  function clearThreadFilter() {
@@ -3155,6 +3301,11 @@ function toggleSidebar() {
3155
3301
  document.getElementById('sidebar-overlay').classList.toggle('open');
3156
3302
  }
3157
3303
 
3304
+ function closeSidebar() {
3305
+ document.getElementById('sidebar').classList.remove('open');
3306
+ document.getElementById('sidebar-overlay').classList.remove('open');
3307
+ }
3308
+
3158
3309
  var newWhileScrolled = 0;
3159
3310
 
3160
3311
  document.getElementById('messages').addEventListener('scroll', function() {
@@ -3610,6 +3761,8 @@ function switchView(view) {
3610
3761
  if (view === 'workspaces') fetchWorkspaces();
3611
3762
  if (view === 'workflows') fetchWorkflows();
3612
3763
  if (view === 'launch') renderLaunchPanel();
3764
+ // Auto-close sidebar on mobile after view switch
3765
+ if (isMobile) closeSidebar();
3613
3766
  }
3614
3767
 
3615
3768
  // ==================== TASKS ====================
@@ -4095,6 +4248,7 @@ function poll() {
4095
4248
  fetch('/api/agents' + pq).then(function(r) { return r.json(); }),
4096
4249
  fetch('/api/status' + pq).then(function(r) { return r.json(); }),
4097
4250
  ]).then(function(results) {
4251
+ console.log('[LTT] poll ok — history:' + results[0].length + ' agents:' + Object.keys(results[1]).length + ' project:' + (activeProject || 'default'));
4098
4252
  updateConnectionInfo(Date.now() - pollStart);
4099
4253
  var prevHistLen = cachedHistory.length;
4100
4254
  cachedHistory = results[0];
@@ -4140,7 +4294,7 @@ function poll() {
4140
4294
  if (activeView === 'workflows') fetchWorkflows();
4141
4295
  }).catch(function(e) {
4142
4296
  console.error('Poll failed:', e);
4143
- document.getElementById('conn-detail').textContent = ' err';
4297
+ document.getElementById('conn-detail').textContent = ' ERR: ' + e.message;
4144
4298
  });
4145
4299
  }
4146
4300
 
@@ -4158,7 +4312,8 @@ function doReset() {
4158
4312
  // ==================== PROJECT MANAGEMENT ====================
4159
4313
 
4160
4314
  function loadProjects() {
4161
- fetch('/api/projects').then(function(r) { return r.json(); }).then(function(projects) {
4315
+ return fetch('/api/projects').then(function(r) { return r.json(); }).then(function(projects) {
4316
+ console.log('[LTT] loadProjects:', projects.length, 'projects, activeProject:', activeProject);
4162
4317
  var sel = document.getElementById('project-select');
4163
4318
  // Keep the first option (Default)
4164
4319
  while (sel.options.length > 1) sel.remove(1);
@@ -4168,18 +4323,76 @@ function loadProjects() {
4168
4323
  opt.textContent = projects[i].name;
4169
4324
  sel.appendChild(opt);
4170
4325
  }
4171
- if (activeProject) sel.value = activeProject;
4326
+
4327
+ // Auto-select first project if none selected
4328
+ if (!activeProject && projects.length > 0) {
4329
+ activeProject = projects[0].path;
4330
+ console.log('[LTT] auto-selected first project:', activeProject);
4331
+ }
4332
+
4333
+ // If project came from URL param, try fuzzy match (handles path separator differences)
4334
+ if (activeProject && sel.value !== activeProject) {
4335
+ var normalizedActive = activeProject.replace(/\\/g, '/').toLowerCase();
4336
+ for (var j = 0; j < projects.length; j++) {
4337
+ if (projects[j].path.replace(/\\/g, '/').toLowerCase() === normalizedActive) {
4338
+ activeProject = projects[j].path; // use server's canonical path
4339
+ break;
4340
+ }
4341
+ }
4342
+ }
4343
+
4344
+ if (activeProject) {
4345
+ sel.value = activeProject;
4346
+ document.getElementById('remove-project-btn').style.display = '';
4347
+ // Update header project indicator for mobile
4348
+ var indicator = document.getElementById('mobile-project-name');
4349
+ if (indicator) {
4350
+ var proj = projects.find(function(p) { return p.path === activeProject; });
4351
+ indicator.textContent = proj ? proj.name : '';
4352
+ indicator.style.display = proj ? '' : 'none';
4353
+ }
4354
+ }
4172
4355
 
4173
4356
  // Show/hide remove button
4174
4357
  document.getElementById('remove-project-btn').style.display = activeProject ? '' : 'none';
4175
- }).catch(function() {});
4358
+ // Sync mobile header project select
4359
+ syncMobileProjectSelect();
4360
+ }).catch(function(e) {
4361
+ console.error('[LTT] loadProjects failed:', e);
4362
+ });
4176
4363
  }
4177
4364
 
4178
4365
  function switchProject() {
4179
4366
  activeProject = document.getElementById('project-select').value;
4180
4367
  lastMessageCount = 0;
4181
4368
  document.getElementById('remove-project-btn').style.display = activeProject ? '' : 'none';
4369
+ syncMobileProjectSelect();
4182
4370
  poll();
4371
+ if (isMobile) closeSidebar();
4372
+ }
4373
+
4374
+ function mobileSelectProject(val) {
4375
+ activeProject = val;
4376
+ lastMessageCount = 0;
4377
+ // Sync sidebar select
4378
+ document.getElementById('project-select').value = val;
4379
+ document.getElementById('remove-project-btn').style.display = val ? '' : 'none';
4380
+ poll();
4381
+ }
4382
+
4383
+ function syncMobileProjectSelect() {
4384
+ var mobileSel = document.getElementById('mobile-project-select');
4385
+ if (!mobileSel) return;
4386
+ var sidebarSel = document.getElementById('project-select');
4387
+ // Copy options from sidebar select to mobile select
4388
+ while (mobileSel.options.length > 1) mobileSel.remove(1);
4389
+ for (var i = 1; i < sidebarSel.options.length; i++) {
4390
+ var opt = document.createElement('option');
4391
+ opt.value = sidebarSel.options[i].value;
4392
+ opt.textContent = sidebarSel.options[i].textContent;
4393
+ mobileSel.appendChild(opt);
4394
+ }
4395
+ mobileSel.value = activeProject || '';
4183
4396
  }
4184
4397
 
4185
4398
  function showAddProject() {
@@ -4225,7 +4438,7 @@ function discoverProjects() {
4225
4438
 
4226
4439
  fetch('/api/discover').then(function(r) { return r.json(); }).then(function(found) {
4227
4440
  if (!found.length) {
4228
- resultsEl.innerHTML = '<div style="font-size:11px;color:var(--text-muted);padding:4px;">No new projects found nearby</div>';
4441
+ resultsEl.innerHTML = '<div style="font-size:11px;color:var(--text-muted);padding:4px;">No new projects found (all discovered projects already added)</div>';
4229
4442
  setTimeout(function() { resultsEl.style.display = 'none'; }, 3000);
4230
4443
  return;
4231
4444
  }
@@ -4471,9 +4684,10 @@ function renderReplayMessages() {
4471
4684
  // ==================== BROWSER NOTIFICATIONS ====================
4472
4685
 
4473
4686
  var notifEnabled = localStorage.getItem('ltt-notif') === 'true';
4474
- var notifPermission = Notification ? Notification.permission : 'denied';
4687
+ var notifPermission = (typeof Notification !== 'undefined') ? Notification.permission : 'denied';
4475
4688
 
4476
4689
  function toggleNotifications() {
4690
+ if (typeof Notification === 'undefined') return;
4477
4691
  if (!notifEnabled) {
4478
4692
  if (notifPermission !== 'granted') {
4479
4693
  Notification.requestPermission().then(function(perm) {
@@ -4507,7 +4721,7 @@ function updateNotifBtn() {
4507
4721
  }
4508
4722
 
4509
4723
  function sendBrowserNotification(msg) {
4510
- if (!notifEnabled || notifPermission !== 'granted') return;
4724
+ if (typeof Notification === 'undefined' || !notifEnabled || notifPermission !== 'granted') return;
4511
4725
  if (document.hasFocus()) return; // Only notify when tab is in background
4512
4726
  try {
4513
4727
  var preview = msg.content.substring(0, 80);
@@ -4802,12 +5016,20 @@ function initSSE() {
4802
5016
  var projectFromUrl = params.get('project');
4803
5017
  if (projectFromUrl) {
4804
5018
  activeProject = projectFromUrl;
5019
+ console.log('[LTT] project from URL:', projectFromUrl);
4805
5020
  }
4806
5021
  })();
4807
5022
 
4808
- loadProjects();
4809
- poll();
4810
- initSSE();
5023
+ // Load projects first, then poll (so auto-select works before first data fetch)
5024
+ loadProjects().then(function() {
5025
+ console.log('[LTT] init: projects loaded, starting poll + SSE');
5026
+ poll();
5027
+ initSSE();
5028
+ }).catch(function(e) {
5029
+ console.error('[LTT] init: loadProjects failed, polling anyway:', e);
5030
+ poll();
5031
+ initSSE();
5032
+ });
4811
5033
  // Safety-net poll at 10s (SSE handles real-time, this catches any missed updates)
4812
5034
  setInterval(poll, 10000);
4813
5035
  </script>
package/dashboard.js CHANGED
@@ -6,7 +6,12 @@ const os = require('os');
6
6
  const { spawn } = require('child_process');
7
7
 
8
8
  const PORT = parseInt(process.env.AGENT_BRIDGE_PORT || '3000', 10);
9
- let LAN_MODE = process.env.AGENT_BRIDGE_LAN === 'true';
9
+ const LAN_STATE_FILE = path.join(__dirname, '.lan-mode');
10
+ let LAN_MODE = process.env.AGENT_BRIDGE_LAN === 'true' || (fs.existsSync(LAN_STATE_FILE) && fs.readFileSync(LAN_STATE_FILE, 'utf8').trim() === 'true');
11
+
12
+ function persistLanMode() {
13
+ try { fs.writeFileSync(LAN_STATE_FILE, LAN_MODE ? 'true' : 'false'); } catch {}
14
+ }
10
15
 
11
16
  function getLanIP() {
12
17
  const interfaces = os.networkInterfaces();
@@ -422,7 +427,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;backgrou
422
427
  <div class="agent-chips" id="agent-chips"></div>
423
428
  </div>
424
429
  <div class="messages" id="messages"></div>
425
- <div class="footer">Generated by <a href="https://github.com/Dekelelz/let-them-talk" target="_blank">Let Them Talk</a> &middot; MIT License</div>
430
+ <div class="footer">Generated by <a href="https://github.com/Dekelelz/let-them-talk" target="_blank">Let Them Talk</a> &middot; BSL 1.1</div>
426
431
  <script>
427
432
  var COLORS=['#58a6ff','#3fb950','#d29922','#f85149','#bc8cff','#f778ba','#79c0ff','#7ee787','#e3b341','#ffa198'];
428
433
  var colorMap={},ci=0;
@@ -539,8 +544,9 @@ function apiDiscover() {
539
544
  const checked = new Set();
540
545
  const existing = new Set(getProjects().map(p => p.path));
541
546
 
542
- function scanDir(dir, depth) {
543
- if (depth > 2 || checked.has(dir)) return;
547
+ function scanDir(dir, depth, maxDepth) {
548
+ maxDepth = maxDepth || 3;
549
+ if (depth > maxDepth || checked.has(dir)) return;
544
550
  checked.add(dir);
545
551
  try {
546
552
  const entries = fs.readdirSync(dir, { withFileTypes: true });
@@ -554,17 +560,26 @@ function apiDiscover() {
554
560
  if (!existing.has(projectPath)) {
555
561
  found.push({ name: path.basename(projectPath), path: projectPath, dataDir: fullPath });
556
562
  }
557
- } else if (depth < 2) {
558
- scanDir(fullPath, depth + 1);
563
+ } else if (depth < maxDepth) {
564
+ scanDir(fullPath, depth + 1, maxDepth);
559
565
  }
560
566
  }
561
567
  } catch {}
562
568
  }
563
569
 
564
- // Scan from cwd, parent, and home
570
+ // Scan from cwd, parent, home, Desktop, and common project locations
565
571
  const cwd = process.cwd();
572
+ const home = process.env.HOME || process.env.USERPROFILE || '';
566
573
  scanDir(cwd, 0);
567
574
  scanDir(path.dirname(cwd), 1);
575
+ if (home) {
576
+ scanDir(home, 0);
577
+ scanDir(path.join(home, 'Desktop'), 0);
578
+ scanDir(path.join(home, 'Documents'), 0);
579
+ scanDir(path.join(home, 'Projects'), 0);
580
+ scanDir(path.join(home, 'Desktop', 'Claude Projects'), 0);
581
+ scanDir(path.join(home, 'Desktop', 'Projects'), 0);
582
+ }
568
583
 
569
584
  return found;
570
585
  }
@@ -736,12 +751,15 @@ const server = http.createServer(async (req, res) => {
736
751
  return;
737
752
  }
738
753
 
739
- // Serve dashboard HTML (re-read in dev mode for hot reload)
754
+ // Serve dashboard HTML (always re-read for hot reload)
740
755
  if (url.pathname === '/' || url.pathname === '/index.html') {
741
- const html = process.env.NODE_ENV === 'development'
742
- ? fs.readFileSync(HTML_FILE, 'utf8')
743
- : htmlContent;
744
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
756
+ const html = fs.readFileSync(HTML_FILE, 'utf8');
757
+ res.writeHead(200, {
758
+ 'Content-Type': 'text/html; charset=utf-8',
759
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
760
+ 'Pragma': 'no-cache',
761
+ 'Expires': '0'
762
+ });
745
763
  res.end(html);
746
764
  }
747
765
  // Existing APIs (now with ?project= param support)
@@ -945,6 +963,7 @@ const server = http.createServer(async (req, res) => {
945
963
  const newMode = !LAN_MODE;
946
964
  const lanIP = getLanIP();
947
965
  LAN_MODE = newMode;
966
+ persistLanMode();
948
967
  // Send response first
949
968
  res.writeHead(200, { 'Content-Type': 'application/json' });
950
969
  res.end(JSON.stringify({ lan_mode: newMode, lan_ip: lanIP, port: PORT }));
@@ -1062,7 +1081,7 @@ server.listen(PORT, LAN_MODE ? '0.0.0.0' : '127.0.0.1', () => {
1062
1081
  const dataDir = resolveDataDir();
1063
1082
  const lanIP = getLanIP();
1064
1083
  console.log('');
1065
- console.log(' Let Them Talk - Agent Bridge Dashboard v3.0');
1084
+ console.log(' Let Them Talk - Agent Bridge Dashboard v3.3.3');
1066
1085
  console.log(' ============================================');
1067
1086
  console.log(' Dashboard: http://localhost:' + PORT);
1068
1087
  if (LAN_MODE && lanIP) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "let-them-talk",
3
- "version": "3.3.1",
3
+ "version": "3.3.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": {
@@ -47,8 +47,8 @@
47
47
  "bugs": {
48
48
  "url": "https://github.com/Dekelelz/let-them-talk/issues"
49
49
  },
50
- "author": "Dekelelz",
51
- "license": "MIT",
50
+ "author": "Dekelelz <contact@talk.unrealai.studio>",
51
+ "license": "SEE LICENSE IN LICENSE",
52
52
  "dependencies": {
53
53
  "@modelcontextprotocol/sdk": "1.27.1"
54
54
  }
package/server.js CHANGED
@@ -2021,7 +2021,7 @@ async function main() {
2021
2021
  loadPlugins();
2022
2022
  const transport = new StdioServerTransport();
2023
2023
  await server.connect(transport);
2024
- console.error('Agent Bridge MCP server v3.3.1 running (' + (27 + loadedPlugins.length) + ' tools)');
2024
+ console.error('Agent Bridge MCP server v3.3.3 running (' + (27 + loadedPlugins.length) + ' tools)');
2025
2025
  }
2026
2026
 
2027
2027
  main().catch(console.error);