let-them-talk 3.3.2 → 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 +1 -1
- package/LICENSE +2 -2
- package/README.md +205 -150
- package/SECURITY.md +1 -1
- package/cli.js +1 -1
- package/dashboard.html +229 -29
- package/dashboard.js +24 -11
- package/package.json +3 -3
- package/server.js +1 -1
package/CHANGELOG.md
CHANGED
package/LICENSE
CHANGED
|
@@ -6,7 +6,7 @@ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
|
|
|
6
6
|
Parameters
|
|
7
7
|
|
|
8
8
|
Licensor: Dekelelz
|
|
9
|
-
Licensed Work: Let Them Talk v3.3.
|
|
9
|
+
Licensed Work: Let Them Talk v3.3.3
|
|
10
10
|
The Licensed Work is (c) 2024-2026 Dekelelz.
|
|
11
11
|
Additional Use Grant: You may make use of the Licensed Work, provided that
|
|
12
12
|
you may not use the Licensed Work for a Commercial
|
|
@@ -27,7 +27,7 @@ Change Date: 2028-03-14
|
|
|
27
27
|
Change License: Apache License, Version 2.0
|
|
28
28
|
|
|
29
29
|
For information about alternative licensing arrangements for the Licensed Work,
|
|
30
|
-
please contact:
|
|
30
|
+
please contact: contact@talk.unrealai.studio
|
|
31
31
|
|
|
32
32
|
Notice
|
|
33
33
|
|
package/README.md
CHANGED
|
@@ -1,63 +1,148 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="agent-bridge/logo.png" alt="Let Them Talk" width="120">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
[](https://github.com/Dekelelz/let-them-talk/blob/master/LICENSE)
|
|
5
|
-
[](https://discord.gg/6Y9YgkFNJP)
|
|
5
|
+
<h1 align="center">Let Them Talk</h1>
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
# 1. Install in any project
|
|
15
|
-
npx let-them-talk init
|
|
28
|
+
---
|
|
16
29
|
|
|
17
|
-
|
|
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
|
-
|
|
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.
|
|
23
33
|
|
|
24
|
-
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
Preferred setup: one command to install, one to launch the dashboard.
|
|
25
37
|
|
|
26
38
|
```bash
|
|
27
|
-
npx let-them-talk init
|
|
28
|
-
npx let-them-talk
|
|
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
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
52
81
|
```
|
|
53
82
|
|
|
54
|
-
Each
|
|
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.
|
|
55
84
|
|
|
56
|
-
##
|
|
85
|
+
## Highlights
|
|
57
86
|
|
|
58
|
-
|
|
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
|
|
59
97
|
|
|
60
|
-
|
|
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
|
|
108
|
+
```
|
|
109
|
+
|
|
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:**
|
|
122
|
+
|
|
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
|
|
127
|
+
|
|
128
|
+
**Plus:**
|
|
129
|
+
|
|
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
|
|
75
|
-
| `share_file` | Send file contents
|
|
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
|
-
|
|
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
|
|
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 |
|
|
176
|
+
|
|
177
|
+
</details>
|
|
88
178
|
|
|
89
|
-
|
|
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
|
|
95
|
-
| `workspace_read` | Read workspace
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
+
```
|
|
231
|
+
|
|
232
|
+
Plugins run sandboxed with a 30-second timeout. Manage via CLI or dashboard.
|
|
198
233
|
|
|
199
|
-
##
|
|
234
|
+
## CLI Reference
|
|
200
235
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
##
|
|
216
|
-
|
|
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.
|
|
218
|
-
|
|
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
|
|
278
|
+
## Contributing
|
|
224
279
|
|
|
225
|
-
|
|
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
|
|
280
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
234
281
|
|
|
235
|
-
|
|
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.
|
|
282
|
+
## Contact
|
|
237
283
|
|
|
238
|
-
|
|
239
|
-
Plugins run with full Node.js access. Only install plugins you trust. This is the same trust model as npm packages.
|
|
284
|
+
For business inquiries, licensing, and partnerships: **contact@talk.unrealai.studio**
|
|
240
285
|
|
|
241
286
|
## License
|
|
242
287
|
|
|
243
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.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
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 **
|
|
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
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:
|
|
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-
|
|
1819
|
+
flex-direction: column;
|
|
1820
|
+
padding: 8px 10px;
|
|
1821
|
+
gap: 6px;
|
|
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;
|
|
1774
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; }
|
|
1775
1843
|
|
|
1776
|
-
|
|
1844
|
+
/* Search bar */
|
|
1845
|
+
.search-input { font-size: 14px; padding: 8px 10px; }
|
|
1777
1846
|
|
|
1778
|
-
|
|
1847
|
+
/* Phone modal: full-width on mobile */
|
|
1848
|
+
.phone-modal { width: 95vw; max-width: 95vw; }
|
|
1849
|
+
|
|
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
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
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,7 +2435,10 @@
|
|
|
2308
2435
|
<button class="mobile-toggle" onclick="toggleSidebar()" aria-label="Menu">☰</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>
|
|
2311
|
-
<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"></span>
|
|
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>
|
|
2312
2442
|
<div class="header-stats">
|
|
2313
2443
|
<div class="h-stat"><span class="h-stat-val" id="stat-messages">0</span> msgs</div>
|
|
2314
2444
|
<div class="h-stat"><span class="h-stat-val" id="stat-agents">0</span> agents</div>
|
|
@@ -2472,7 +2602,7 @@
|
|
|
2472
2602
|
</div>
|
|
2473
2603
|
</div>
|
|
2474
2604
|
<div class="app-footer">
|
|
2475
|
-
<span>Let Them Talk v3.3.
|
|
2605
|
+
<span>Let Them Talk v3.3.3</span>
|
|
2476
2606
|
</div>
|
|
2477
2607
|
<div class="profile-popup" id="profile-popup" onclick="event.stopPropagation()">
|
|
2478
2608
|
<div class="profile-popup-header">
|
|
@@ -2531,6 +2661,8 @@ var activeThread = null;
|
|
|
2531
2661
|
var activeProject = ''; // empty = default/local
|
|
2532
2662
|
var cachedHistory = [];
|
|
2533
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; });
|
|
2534
2666
|
|
|
2535
2667
|
// Agent color palette
|
|
2536
2668
|
var COLORS = [
|
|
@@ -2970,20 +3102,32 @@ function renderThreads(messages) {
|
|
|
2970
3102
|
function renderMessages(messages) {
|
|
2971
3103
|
var el = document.getElementById('messages');
|
|
2972
3104
|
|
|
3105
|
+
|
|
2973
3106
|
if (!messages.length) {
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
'<div class="
|
|
2979
|
-
'<div class="
|
|
2980
|
-
'<div class="
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
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">💬</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">💬</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 "A". 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 "B". 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
|
+
}
|
|
2987
3131
|
return;
|
|
2988
3132
|
}
|
|
2989
3133
|
|
|
@@ -3142,6 +3286,7 @@ function filterThread(tid) {
|
|
|
3142
3286
|
lastMessageCount = 0;
|
|
3143
3287
|
renderThreads(cachedHistory);
|
|
3144
3288
|
renderMessages(cachedHistory);
|
|
3289
|
+
if (isMobile) closeSidebar();
|
|
3145
3290
|
}
|
|
3146
3291
|
|
|
3147
3292
|
function clearThreadFilter() {
|
|
@@ -3156,6 +3301,11 @@ function toggleSidebar() {
|
|
|
3156
3301
|
document.getElementById('sidebar-overlay').classList.toggle('open');
|
|
3157
3302
|
}
|
|
3158
3303
|
|
|
3304
|
+
function closeSidebar() {
|
|
3305
|
+
document.getElementById('sidebar').classList.remove('open');
|
|
3306
|
+
document.getElementById('sidebar-overlay').classList.remove('open');
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3159
3309
|
var newWhileScrolled = 0;
|
|
3160
3310
|
|
|
3161
3311
|
document.getElementById('messages').addEventListener('scroll', function() {
|
|
@@ -3611,6 +3761,8 @@ function switchView(view) {
|
|
|
3611
3761
|
if (view === 'workspaces') fetchWorkspaces();
|
|
3612
3762
|
if (view === 'workflows') fetchWorkflows();
|
|
3613
3763
|
if (view === 'launch') renderLaunchPanel();
|
|
3764
|
+
// Auto-close sidebar on mobile after view switch
|
|
3765
|
+
if (isMobile) closeSidebar();
|
|
3614
3766
|
}
|
|
3615
3767
|
|
|
3616
3768
|
// ==================== TASKS ====================
|
|
@@ -4172,9 +4324,21 @@ function loadProjects() {
|
|
|
4172
4324
|
sel.appendChild(opt);
|
|
4173
4325
|
}
|
|
4174
4326
|
|
|
4175
|
-
// Auto-select
|
|
4176
|
-
if (!activeProject && projects.length
|
|
4327
|
+
// Auto-select first project if none selected
|
|
4328
|
+
if (!activeProject && projects.length > 0) {
|
|
4177
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
|
+
}
|
|
4178
4342
|
}
|
|
4179
4343
|
|
|
4180
4344
|
if (activeProject) {
|
|
@@ -4187,21 +4351,50 @@ function loadProjects() {
|
|
|
4187
4351
|
indicator.textContent = proj ? proj.name : '';
|
|
4188
4352
|
indicator.style.display = proj ? '' : 'none';
|
|
4189
4353
|
}
|
|
4190
|
-
poll(); // re-poll with correct project
|
|
4191
4354
|
}
|
|
4192
4355
|
|
|
4193
4356
|
// Show/hide remove button
|
|
4194
4357
|
document.getElementById('remove-project-btn').style.display = activeProject ? '' : 'none';
|
|
4195
|
-
|
|
4358
|
+
// Sync mobile header project select
|
|
4359
|
+
syncMobileProjectSelect();
|
|
4360
|
+
}).catch(function(e) {
|
|
4361
|
+
console.error('[LTT] loadProjects failed:', e);
|
|
4362
|
+
});
|
|
4196
4363
|
}
|
|
4197
4364
|
|
|
4198
4365
|
function switchProject() {
|
|
4199
4366
|
activeProject = document.getElementById('project-select').value;
|
|
4200
4367
|
lastMessageCount = 0;
|
|
4201
4368
|
document.getElementById('remove-project-btn').style.display = activeProject ? '' : 'none';
|
|
4369
|
+
syncMobileProjectSelect();
|
|
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';
|
|
4202
4380
|
poll();
|
|
4203
4381
|
}
|
|
4204
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 || '';
|
|
4396
|
+
}
|
|
4397
|
+
|
|
4205
4398
|
function showAddProject() {
|
|
4206
4399
|
var input = document.getElementById('project-path-input');
|
|
4207
4400
|
if (input.classList.contains('visible')) {
|
|
@@ -4245,7 +4438,7 @@ function discoverProjects() {
|
|
|
4245
4438
|
|
|
4246
4439
|
fetch('/api/discover').then(function(r) { return r.json(); }).then(function(found) {
|
|
4247
4440
|
if (!found.length) {
|
|
4248
|
-
resultsEl.innerHTML = '<div style="font-size:11px;color:var(--text-muted);padding:4px;">No new projects found
|
|
4441
|
+
resultsEl.innerHTML = '<div style="font-size:11px;color:var(--text-muted);padding:4px;">No new projects found (all discovered projects already added)</div>';
|
|
4249
4442
|
setTimeout(function() { resultsEl.style.display = 'none'; }, 3000);
|
|
4250
4443
|
return;
|
|
4251
4444
|
}
|
|
@@ -4491,9 +4684,10 @@ function renderReplayMessages() {
|
|
|
4491
4684
|
// ==================== BROWSER NOTIFICATIONS ====================
|
|
4492
4685
|
|
|
4493
4686
|
var notifEnabled = localStorage.getItem('ltt-notif') === 'true';
|
|
4494
|
-
var notifPermission = Notification ? Notification.permission : 'denied';
|
|
4687
|
+
var notifPermission = (typeof Notification !== 'undefined') ? Notification.permission : 'denied';
|
|
4495
4688
|
|
|
4496
4689
|
function toggleNotifications() {
|
|
4690
|
+
if (typeof Notification === 'undefined') return;
|
|
4497
4691
|
if (!notifEnabled) {
|
|
4498
4692
|
if (notifPermission !== 'granted') {
|
|
4499
4693
|
Notification.requestPermission().then(function(perm) {
|
|
@@ -4527,7 +4721,7 @@ function updateNotifBtn() {
|
|
|
4527
4721
|
}
|
|
4528
4722
|
|
|
4529
4723
|
function sendBrowserNotification(msg) {
|
|
4530
|
-
if (!notifEnabled || notifPermission !== 'granted') return;
|
|
4724
|
+
if (typeof Notification === 'undefined' || !notifEnabled || notifPermission !== 'granted') return;
|
|
4531
4725
|
if (document.hasFocus()) return; // Only notify when tab is in background
|
|
4532
4726
|
try {
|
|
4533
4727
|
var preview = msg.content.substring(0, 80);
|
|
@@ -4822,11 +5016,17 @@ function initSSE() {
|
|
|
4822
5016
|
var projectFromUrl = params.get('project');
|
|
4823
5017
|
if (projectFromUrl) {
|
|
4824
5018
|
activeProject = projectFromUrl;
|
|
5019
|
+
console.log('[LTT] project from URL:', projectFromUrl);
|
|
4825
5020
|
}
|
|
4826
5021
|
})();
|
|
4827
5022
|
|
|
4828
5023
|
// Load projects first, then poll (so auto-select works before first data fetch)
|
|
4829
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);
|
|
4830
5030
|
poll();
|
|
4831
5031
|
initSSE();
|
|
4832
5032
|
});
|
package/dashboard.js
CHANGED
|
@@ -544,8 +544,9 @@ function apiDiscover() {
|
|
|
544
544
|
const checked = new Set();
|
|
545
545
|
const existing = new Set(getProjects().map(p => p.path));
|
|
546
546
|
|
|
547
|
-
function scanDir(dir, depth) {
|
|
548
|
-
|
|
547
|
+
function scanDir(dir, depth, maxDepth) {
|
|
548
|
+
maxDepth = maxDepth || 3;
|
|
549
|
+
if (depth > maxDepth || checked.has(dir)) return;
|
|
549
550
|
checked.add(dir);
|
|
550
551
|
try {
|
|
551
552
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
@@ -559,17 +560,26 @@ function apiDiscover() {
|
|
|
559
560
|
if (!existing.has(projectPath)) {
|
|
560
561
|
found.push({ name: path.basename(projectPath), path: projectPath, dataDir: fullPath });
|
|
561
562
|
}
|
|
562
|
-
} else if (depth <
|
|
563
|
-
scanDir(fullPath, depth + 1);
|
|
563
|
+
} else if (depth < maxDepth) {
|
|
564
|
+
scanDir(fullPath, depth + 1, maxDepth);
|
|
564
565
|
}
|
|
565
566
|
}
|
|
566
567
|
} catch {}
|
|
567
568
|
}
|
|
568
569
|
|
|
569
|
-
// Scan from cwd, parent, and
|
|
570
|
+
// Scan from cwd, parent, home, Desktop, and common project locations
|
|
570
571
|
const cwd = process.cwd();
|
|
572
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
571
573
|
scanDir(cwd, 0);
|
|
572
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
|
+
}
|
|
573
583
|
|
|
574
584
|
return found;
|
|
575
585
|
}
|
|
@@ -741,12 +751,15 @@ const server = http.createServer(async (req, res) => {
|
|
|
741
751
|
return;
|
|
742
752
|
}
|
|
743
753
|
|
|
744
|
-
// Serve dashboard HTML (re-read
|
|
754
|
+
// Serve dashboard HTML (always re-read for hot reload)
|
|
745
755
|
if (url.pathname === '/' || url.pathname === '/index.html') {
|
|
746
|
-
const html =
|
|
747
|
-
|
|
748
|
-
:
|
|
749
|
-
|
|
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
|
+
});
|
|
750
763
|
res.end(html);
|
|
751
764
|
}
|
|
752
765
|
// Existing APIs (now with ?project= param support)
|
|
@@ -1068,7 +1081,7 @@ server.listen(PORT, LAN_MODE ? '0.0.0.0' : '127.0.0.1', () => {
|
|
|
1068
1081
|
const dataDir = resolveDataDir();
|
|
1069
1082
|
const lanIP = getLanIP();
|
|
1070
1083
|
console.log('');
|
|
1071
|
-
console.log(' Let Them Talk - Agent Bridge Dashboard v3.3.
|
|
1084
|
+
console.log(' Let Them Talk - Agent Bridge Dashboard v3.3.3');
|
|
1072
1085
|
console.log(' ============================================');
|
|
1073
1086
|
console.log(' Dashboard: http://localhost:' + PORT);
|
|
1074
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.
|
|
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": "
|
|
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.
|
|
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);
|