clay-server 2.35.1 → 2.36.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,173 +1,221 @@
1
1
  <p align="center">
2
- <img src="media/logo/icon-full-banded-256-transparent.png" alt="Clay" />
2
+ <img src="media/logo/icon-full-banded-256-transparent.png" alt="Clay - self-hosted team workspace for Claude Code and Codex" />
3
3
  </p>
4
4
 
5
- <h3 align="center">Claude Code for your whole team. No team? Build one with AI.</h3>
5
+ <h2 align="center">Multiplayer Claude Code and Codex.</h2>
6
+ <h4 align="center">
7
+ Drop into a teammate's session live.<br>
8
+ Or pair with AI mates that remember and push back.<br>
9
+ Self-hosted, one toggle between vendors.
10
+ </h4>
6
11
 
7
- [![npm version](https://img.shields.io/npm/v/clay-server)](https://www.npmjs.com/package/clay-server) [![npm downloads](https://img.shields.io/npm/dw/clay-server)](https://www.npmjs.com/package/clay-server) [![GitHub stars](https://img.shields.io/github/stars/chadbyte/clay)](https://github.com/chadbyte/clay) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/chadbyte/clay/blob/main/LICENSE)
12
+ <p align="center">
13
+ <a href="https://www.npmjs.com/package/clay-server"><img src="https://img.shields.io/npm/v/clay-server" alt="npm version" /></a>
14
+ <a href="https://www.npmjs.com/package/clay-server"><img src="https://img.shields.io/npm/dw/clay-server" alt="npm downloads" /></a>
15
+ <a href="https://github.com/chadbyte/clay"><img src="https://img.shields.io/github/stars/chadbyte/clay" alt="GitHub stars" /></a>
16
+ <a href="https://github.com/chadbyte/clay/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT" /></a>
17
+ </p>
18
+
19
+ The browser tab your whole team lives in. Humans, AI agents, projects, sessions, decisions.
8
20
 
9
- <p align="center"><img src="media/hero.png" alt="Clay workspace" /></p>
21
+ Afraid of locking your personal or team knowledge into one vendor? With Clay, your conventions, sessions, and decisions live on your disk. Switch when you want.
10
22
 
11
- Everything Claude Code does, in your browser and on your phone. Multi-session, multi-user, self-hosted. No cloud relay, no middleman.
23
+ *A team-shared, self-hosted GUI for Claude Code and Codex CLIs.*
12
24
 
13
25
  ```bash
14
26
  npx clay-server
15
27
  # Scan the QR code to connect from any device
16
28
  ```
17
29
 
18
- ---
30
+ ## What's Clay?
19
31
 
20
- ## What you get
32
+ - **A browser-based workspace for Claude Code and Codex.** Open it on any device, alone or with your whole team.
33
+ - **A place where your team collaborates across projects.** Engineers, PMs, designers, and domain experts share one workspace, hop between projects, drop into each other's sessions.
34
+ - **Your virtual team.** Mates with names, memory, and roles — architect, reviewer, designer, whoever you need. They learn your codebase, push back on bad ideas, and don't reset between sessions.
35
+ - **A multi-project dashboard.** Every repo on your machine in one sidebar. Run agents across several in parallel; permission requests and completions surface as notifications.
36
+ - **A self-hosted dev server.** Runs on your machine in plain JSONL and Markdown. No proprietary database, no cloud relay, no lock-in. Walk away whenever — your data walks with you.
37
+ - **A work-automation system.** Ralph Loop iterates a feature overnight; cron schedules agents while you're away. Wake up to results, or a clean failure trace.
21
38
 
22
- ### Drop-in replacement for the CLI
39
+ ## What it does
23
40
 
24
- Your CLI sessions, your CLAUDE.md rules, your MCP servers. **All of it works in Clay as-is.** Pick up a CLI session in the browser, or continue a browser session in the CLI.
41
+ ### Run Claude Code and Codex in one workspace
42
+
43
+ Open a session, pick a vendor. Switch sessions, pick the other. Clay's adapter layer (YOKE) speaks the Claude Agent SDK and the Codex app-server protocol natively. Cross-vendor instruction loading: Codex reads AGENTS.md, Claude reads CLAUDE.md, Clay merges the rest into the system prompt automatically.
25
44
 
26
45
  <p align="center">
27
- <img src="media/split.gif" alt="split-screen workflow" width="700">
46
+ <img src="media/split.gif" alt="Toggle between Claude Code and Codex in one click" width="700">
28
47
  </p>
29
48
 
30
- ### Claude Code on steroids
49
+ ### Every project on one dashboard
31
50
 
32
- **Multiple agents, multiple projects, at the same time.** Switch between them in the sidebar. Browse project files live while the agent works, with syntax highlighting for 180+ languages. Mermaid diagrams render as diagrams. Tables render as tables.
51
+ All your projects live in the sidebar. Jump between them in one click, see live status across each, run agents in several at once. No more `cd ~/work/foo && tmux attach && ...`. One Clay daemon hosts every repo on your machine and gives you a single pane of glass over all of them.
33
52
 
34
- **Schedule agents with cron**, or let them run autonomously with **Ralph Loop**. Close your laptop, sessions keep running.
53
+ ### Mates: AI teammates with persistent memory
35
54
 
36
- **Push notifications on mobile.** Your phone buzzes when Claude needs approval, finishes a task, or hits an error. Install as a PWA on iOS or Android, review and approve from anywhere.
55
+ Mates are AI personas with their own CLAUDE.md, knowledge files, and memory that compounds across sessions. They learn your stack, your conventions, your decision history. @mention them mid-session, DM them directly, or drop them into a debate. **They don't flatter you. They push back.**
37
56
 
38
57
  <p align="center">
39
- <img src="media/phone.gif" alt="Clay on phone" width="280">
58
+ <img src="media/mates.gif" alt="An AI teammate (Mate) pushing back on a bad design pitch in Clay" width="640">
40
59
  </p>
41
60
 
42
- ### Your machine, your server, your data
61
+ ### Debate: structured multi-Mate decisions
43
62
 
44
- **Fully local.** Clay runs as a daemon on your machine. Your code and conversations never leave your machine except to reach the AI provider's API.
63
+ Stuck on REST vs GraphQL? Monorepo or split? Surface the question to a debate. Pick panelists, set the format, let your Mates argue both sides with moderated turns. You walk away with a recorded decision, not a vibe check.
45
64
 
46
- **Plain files.** Sessions are JSONL. Settings are JSON. Knowledge is Markdown. Everything lives on your machine in formats you can read, move, and back up. No proprietary database, no cloud lock-in.
65
+ ### Parallel worktrees
47
66
 
48
- **Secure by default.** PIN authentication, per-project permissions, and HTTPS are built in.
67
+ Detect existing git worktrees, spin up new ones from the sidebar, and run agents in each one independently. No more "wait, I have uncommitted changes." Each worktree is an isolated session with its own history.
49
68
 
50
- ### Bring your whole team
69
+ ### Ralph Loop: autonomous coding while you sleep
51
70
 
52
- **One API key runs the whole workspace.** Invite teammates, set permissions per person, per project, per session. Share one key across the org, or let each member use their own Claude Code login.
71
+ Write a `PROMPT.md`, optionally a `JUDGE.md`, hit go. Clay iterates: code, evaluate, retry, until the judge approves or you cap the loop. Run it once, or schedule it on standard Unix cron. Wake up to a finished feature or a clean failure trace.
53
72
 
54
- **OS-level isolation.** On Linux, Clay maps each user to an OS-level account. File permissions and process isolation just work.
73
+ ### Web UI, mobile, push notifications
55
74
 
56
- **Shared sessions.** Your PM describes a bug in plain language, your senior joins the same session, and the fix ships together. If someone gets stuck, **jump into their session** to help in real time.
75
+ Installable PWA on iOS and Android. Push notifications for approvals, errors, and completed tasks. Service worker keeps the app responsive offline. When Claude needs approval, your phone buzzes, you tap approve, the agent keeps going.
57
76
 
58
- ### Build your AI team
77
+ ## Who is Clay for
59
78
 
60
- **Mates.** AI teammates with persistent memory across sessions. They learn your stack, your conventions, and your decision history. @mention them for a quick review, DM them directly, or bring multiple into the same conversation. **They don't flatter you. They push back.**
79
+ ### Small teams (3–10)
61
80
 
62
- <!-- TODO: mates.gif -->
81
+ Engineers, PMs, designers, and domain experts in one workspace. Non-devs read the codebase and ask questions without ever opening an editor. Engineers @mention each other or drop into a teammate's session to help in real time. Share one org-wide API key or let each member bring their own, with costs routing to whoever ran the model.
63
82
 
64
- **Debate.** Your Mates argue both sides before you commit. "REST vs GraphQL?" "Monorepo or separate repos?" "This migration plan won't survive production. Here's why."
83
+ ### Larger teams (10+)
65
84
 
66
- <!-- TODO: debate.gif -->
85
+ Per-user, per-project, per-session permissions. On Linux, opt in to OS-level isolation: each Clay user maps to a real Linux account, file ACLs enforced via `setfacl`, processes spawn under the right UID/GID. Per-project API keys for billing separation. Plain JSONL sessions give you an audit trail you can grep.
67
86
 
68
- ---
87
+ ### Solopreneurs and indie developers
69
88
 
70
- ## Who is Clay for
89
+ You don't have a team yet, so Mates are your team. A persistent architect, a reviewer, a designer — each with their own memory, ready to push back on bad ideas. Run Ralph Loop overnight to ship while you sleep. Toggle Claude and Codex per session to balance cost and capability.
71
90
 
72
- - **Solo dev who needs a second opinion.** Architecture review, dependency decisions, refactor tradeoffs. Build reviewers as Mates instead of asking the void.
73
- - **Small team sharing one Claude Code setup.** One API key, everyone in the browser, no terminal knowledge required.
74
- - **Dev lead running agents overnight.** Schedule tasks with cron, get push notifications on your phone, review in the morning.
91
+ ### Self-hosting developers
92
+
93
+ Your code stays on your machine. Sessions are JSONL, knowledge is Markdown, settings are JSON. No proprietary database, no cloud relay, no middleman. CLAUDE.md, AGENTS.md, `.cursorrules` are all loaded automatically across vendors. Walk away whenever, your data walks with you.
75
94
 
76
95
  ## Getting Started
77
96
 
78
- **Requirements:** Node.js 20+, Claude Code CLI (authenticated).
97
+ **Requirements:** Node.js 20+. Authenticated Claude Code CLI, Codex CLI, or both.
79
98
 
80
99
  ```bash
81
100
  npx clay-server
82
101
  ```
83
102
 
84
- On first run, it asks for a port number and whether you're using it solo or with a team.
85
- Open the browser URL or scan the QR code to connect from your phone instantly.
103
+ On first run, Clay asks for a port and whether you're solo or with a team. Open the URL or scan the QR code from your phone.
86
104
 
87
105
  For remote access, use a VPN like Tailscale.
88
106
 
89
107
  <p align="center">
90
- <img src="media/start.gif" alt="Clay starting from CLI" width="600">
108
+ <img src="media/start.gif" alt="Starting Clay daemon from the CLI with npx clay-server" width="600">
91
109
  </p>
92
110
 
111
+ ## CLI Options
112
+
113
+ ```bash
114
+ npx clay-server # Default (port 2633)
115
+ npx clay-server -p 8080 # Specify port
116
+ npx clay-server --yes # Skip interactive prompts (use defaults)
117
+ npx clay-server -y --pin 123456
118
+ # Non-interactive + PIN (for scripts/CI)
119
+ npx clay-server --add . # Add current directory to running daemon
120
+ npx clay-server --remove . # Remove project
121
+ npx clay-server --list # List registered projects
122
+ npx clay-server --shutdown # Stop running daemon
123
+ npx clay-server --dangerously-skip-permissions
124
+ # Bypass all permission prompts (requires PIN at setup)
125
+ ```
126
+
127
+ Run `npx clay-server --help` for all options.
128
+
93
129
  ## FAQ
94
130
 
95
- **"Is this just a Claude Code wrapper?"**
96
- Clay uses the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) directly. It doesn't wrap terminal output. It adds multi-session orchestration, persistent AI teammates (Mates), structured debates, scheduled agents, multi-user collaboration, and a full browser UI.
131
+ **"Is this a Claude Code wrapper?"**
132
+ No. Clay drives Claude Code through the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) and Codex through the Codex app-server protocol. Both are first-class. Clay adds multi-session orchestration, persistent Mates, structured debates, scheduled agents, multi-user collaboration, built-in MCP servers, and a full browser UI on top.
133
+
134
+ **"Can I run Claude Code and Codex in the same workspace?"**
135
+ Yes. Pick a vendor when you open a session. Switch per session. Same projects, same Mates, same memory.
136
+
137
+ **"How are Mates different from Claude Code's sub-agents?"**
138
+ Sub-agents are ephemeral specialists spawned inside a single Claude Code session — a role and a system prompt for one task, then forgotten when the task ends. Mates are persistent teammates with their own knowledge files and memory that survive every session. A sub-agent forgets you the moment it returns; a Mate remembers your codebase, your decisions, and your conventions across months of work. @mention a Mate mid-session, DM it between sessions, or drop several into a debate.
97
139
 
98
140
  **"Does my code leave my machine?"**
99
- Clay is fully self-hosted. The server runs on your machine, files stay local. Only API calls go out, same as using the CLI directly.
141
+ Only as model API calls (the same as using the CLI directly). Sessions, Mates, knowledge, and settings all stay on disk.
100
142
 
101
- **"Can I continue a CLI session in the browser?"**
102
- Yes. Pick up a CLI session in the browser, or continue a browser session in the CLI.
143
+ **"Does my existing CLAUDE.md / AGENTS.md / .cursorrules work?"**
144
+ Yes. Clay loads native instruction files for each vendor and merges the rest into the system prompt automatically.
103
145
 
104
- **"Does my existing CLAUDE.md work?"**
105
- Yes. If your project has a CLAUDE.md, it works in Clay as-is.
146
+ **"Can I continue a CLI session in the browser?"**
147
+ Yes. CLI sessions show up in the sidebar. Browser sessions can be picked up in the CLI.
106
148
 
107
149
  **"Does each teammate need their own API key?"**
108
- No. Teammates can share one org-wide API key. On Linux with OS-level isolation, each member can also use their own Claude Code login. You can assign different API keys per project for billing isolation.
150
+ No. Share one org-wide key, or let each user bring their own. On Linux with OS-level isolation, each member can also use their own Claude Code or Codex login.
151
+
152
+ **"What does OS-level isolation actually do?"**
153
+ On Linux, opt in and Clay provisions each user as a real Linux account. File ACLs are enforced via `setfacl`, agent processes spawn under the user's UID/GID, and the kernel handles the rest. One teammate can't read another's project files, even by accident. The guarantee comes from the OS, not from a promise in our code.
109
154
 
110
155
  **"Does it work with MCP servers?"**
111
- Yes. MCP configurations from the CLI carry over as-is.
156
+ Yes. User-configured MCPs from `~/.clay/mcp.json` plus built-in browser, email, ask-user, and debate servers. All work in both Claude and Codex sessions.
112
157
 
113
158
  **"Can I use it on my phone?"**
114
- Yes. Clay works as a PWA on iOS and Android. You get push notifications for approvals, errors, and completed tasks. No app store required.
159
+ Yes. Install as a PWA on iOS or Android. Push notifications for approvals, errors, and task completion.
115
160
 
116
161
  **"What is d.clay.studio in my browser URL?"**
117
- It's a DNS-only service that resolves to your local IP for HTTPS certificate validation. No data passes through it. All traffic stays between your browser and your machine. See [clay-dns](clay-dns/) for details.
162
+ A DNS-only service that resolves to your local IP for HTTPS certificate validation. No data passes through it. All traffic stays between your browser and your machine. See [clay-dns](clay-dns/) for details.
118
163
 
119
- ## Why I built Clay
164
+ ## Our Philosophy
120
165
 
121
- Claude Code is the best coding agent I've found. I wanted to turn it into a team, not just a single-player tool.
166
+ One idea: **user experience sovereignty**.
122
167
 
123
- That started as a browser interface so I could access it from anywhere. Then I added multi-user so my team could use it too. Then I started building the AI teammates themselves.
168
+ Not a grand statement. A simple wish: not to have your thinking, your work, and your data locked in the moment a vendor changes a price or rewrites a ToS.
124
169
 
125
- Most AI agent projects go for full autonomy. Let the AI loose, give it all the permissions, let it run. I wanted the opposite: **AI that works as part of a team.** Visible, controllable, accountable. Your teammates can see what the agent is doing, jump in when it needs help, and set the rules it operates under.
170
+ That shows up in the technical choices we made:
126
171
 
127
- That's Clay now. A workspace where AI teammates have names, persistent memory, and their own perspective. Not "act like an expert" prompting. Actual colleagues who remember last week and sit in your sidebar next to the human ones.
172
+ - **Your machine is the server.** Browser your daemon model API. That's the full chain. No vendor cloud, no relay server, no middle tier syncing your sessions through someone else's infrastructure.
173
+ - **One toggle between vendors.** The adapter layer (YOKE) speaks the Claude Agent SDK and the Codex app-server protocol natively. Switching is a setting, not a migration.
174
+ - **Plain text on disk.** Sessions, Mates, knowledge, and settings live as JSONL and Markdown. No proprietary database. You can `cat`, `grep`, version, and back up everything yourself.
175
+ - **Standard formats only.** CLAUDE.md, AGENTS.md, `.cursorrules`, MCP, Unix cron. If you walk away from Clay, your data walks with you in formats every other tool already understands.
128
176
 
129
- ## CLI Options
130
-
131
- ```bash
132
- npx clay-server # Default (port 2633)
133
- npx clay-server -p 8080 # Specify port
134
- npx clay-server --yes # Skip interactive prompts (use defaults)
135
- npx clay-server -y --pin 123456
136
- # Non-interactive + PIN (for scripts/CI)
137
- npx clay-server --add . # Add current directory to running daemon
138
- npx clay-server --remove . # Remove project
139
- npx clay-server --list # List registered projects
140
- npx clay-server --shutdown # Stop running daemon
141
- npx clay-server --dangerously-skip-permissions
142
- # Bypass all permission prompts (requires PIN at setup)
143
- ```
144
-
145
- Run `npx clay-server --help` for all options.
177
+ That's the principle. The rest of the README is what it makes possible.
146
178
 
147
179
  ## Architecture
148
180
 
149
- Clay drives agent execution through the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) and streams it to the browser over WebSocket.
181
+ Clay is a self-hosted daemon. It drives Claude Code (via the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk)) and Codex (via the `codex app-server` JSON-RPC protocol) through a vendor-agnostic adapter layer (**YOKE**), and serves a multi-user web workspace over HTTP/WS. Sessions, Mates, and knowledge live as plain JSONL/Markdown on disk.
150
182
 
151
183
  ```mermaid
152
184
  graph LR
153
- Browser["Browser<br/>(Phone / Desktop)"]
154
- WS["WebSocket"]
155
- Server["HTTP Server<br/>lib/server.js"]
156
- Project["Project Context<br/>lib/project.js"]
157
- SDK["Claude Agent SDK"]
158
- Claude["Claude Code<br/>Process"]
159
- Push["Push Service"]
160
-
161
- Browser <-->|Real time stream| WS
162
- WS <--> Server
163
- Server -->|slug routing| Project
164
- Project <-->|async iterable| SDK
165
- SDK <-->|Prompt / Response| Claude
166
- Project -->|Approval request| Push
167
- Push -->|Notification| Browser
185
+ subgraph Clients
186
+ B1["Browser<br/>User A"]
187
+ B2["Browser<br/>User B"]
188
+ Phone["Phone PWA<br/>+ Push"]
189
+ end
190
+
191
+ subgraph Daemon["Clay Daemon (your machine)"]
192
+ Auth["Auth + RBAC"]
193
+ Server["HTTP / WS Server"]
194
+ Project["Project Context"]
195
+ YOKE["YOKE Adapter Layer"]
196
+ MCP["Built-in MCP servers<br/>ask-user / browser /<br/>debate / email"]
197
+ Push["Push (VAPID)"]
198
+ end
199
+
200
+ subgraph Vendors["Agent runtimes"]
201
+ Claude["Claude Agent SDK"]
202
+ Codex["codex app-server<br/>(JSON-RPC stdio)"]
203
+ end
204
+
205
+ B1 <-->|WS| Server
206
+ B2 <-->|WS| Server
207
+ Phone <-->|WS + push| Server
208
+ Server --> Auth
209
+ Server --> Project
210
+ Project --> YOKE
211
+ Project --> MCP
212
+ Project --> Push
213
+ YOKE --> Claude
214
+ YOKE --> Codex
215
+ Push -.-> Phone
168
216
  ```
169
217
 
170
- For detailed sequence diagrams, daemon architecture, and design decisions, see [docs/architecture.md](docs/architecture.md).
218
+ For sequence diagrams, OS-level isolation, daemon IPC, and key design decisions, see [docs/guides/architecture.md](docs/guides/architecture.md).
171
219
 
172
220
  ## Community Projects
173
221
 
@@ -195,7 +243,7 @@ If you're using Clay, let us know how in Discussions:
195
243
 
196
244
  ## Disclaimer
197
245
 
198
- Not affiliated with Anthropic. Claude is a trademark of Anthropic. Provided "as is" without warranty. Users are responsible for complying with their AI provider's terms of service.
246
+ Not affiliated with Anthropic or OpenAI. Claude is a trademark of Anthropic. Codex is a trademark of OpenAI. Provided "as is" without warranty. Users are responsible for complying with their AI provider's terms of service.
199
247
 
200
248
  ## License
201
249
 
@@ -34,7 +34,7 @@ function attachImage(ctx) {
34
34
  return hydrated;
35
35
  }
36
36
  if (!entry.imageRefs) return entry;
37
- if (entry.type !== "user_message" && entry.type !== "mention_user") return entry;
37
+ if (entry.type !== "user_message" && entry.type !== "mention_user" && entry.type !== "user_mention") return entry;
38
38
  var images = [];
39
39
  for (var ri = 0; ri < entry.imageRefs.length; ri++) {
40
40
  var ref = entry.imageRefs[ri];
@@ -31,6 +31,29 @@ function attachMateInteraction(ctx) {
31
31
  var MENTION_CONTEXT_BUDGET = 32 * 1024; // 32KB total budget for prior turns fed into mention context
32
32
  var MENTION_CONTEXT_MAX_TURNS = 200; // hard safety cap so we never walk the entire session
33
33
 
34
+ // Plain @mention targets: vendor-only mentions with no persona, no memory,
35
+ // no digest write-back. Lets users in a Claude session ask raw Codex (and
36
+ // vice versa) for a second opinion without spinning up a real Mate.
37
+ var PLAIN_MENTIONS = {
38
+ "plain:claude": {
39
+ vendor: "claude",
40
+ name: "Claude Code",
41
+ avatarColor: "#cc785c",
42
+ avatarStyle: "bottts",
43
+ avatarSeed: "plain-claude",
44
+ },
45
+ "plain:codex": {
46
+ vendor: "codex",
47
+ name: "Codex",
48
+ avatarColor: "#10a37f",
49
+ avatarStyle: "bottts",
50
+ avatarSeed: "plain-codex",
51
+ },
52
+ };
53
+ function isPlainMentionId(id) {
54
+ return typeof id === "string" && Object.prototype.hasOwnProperty.call(PLAIN_MENTIONS, id);
55
+ }
56
+
34
57
  // Walk session history backwards collecting full turns (no per-turn truncation)
35
58
  // until either maxTurns is reached or byteBudget is exhausted. Each turn keeps
36
59
  // its full text; we drop older turns first when the budget runs out.
@@ -489,10 +512,27 @@ function attachMateInteraction(ctx) {
489
512
 
490
513
  var userId = ws._clayUser ? ws._clayUser.id : null;
491
514
  var mateCtx = matesModule.buildMateCtx(userId);
492
- var mate = matesModule.getMate(mateCtx, msg.mateId);
493
- if (!mate) {
494
- sendTo(ws, { type: "mention_error", mateId: msg.mateId, error: "Mate not found" });
495
- return;
515
+ var isPlain = isPlainMentionId(msg.mateId);
516
+ var mate;
517
+ if (isPlain) {
518
+ var pCfg = PLAIN_MENTIONS[msg.mateId];
519
+ mate = {
520
+ id: msg.mateId,
521
+ vendor: pCfg.vendor,
522
+ name: pCfg.name,
523
+ profile: {
524
+ displayName: pCfg.name,
525
+ avatarColor: pCfg.avatarColor,
526
+ avatarStyle: pCfg.avatarStyle,
527
+ avatarSeed: pCfg.avatarSeed,
528
+ },
529
+ };
530
+ } else {
531
+ mate = matesModule.getMate(mateCtx, msg.mateId);
532
+ if (!mate) {
533
+ sendTo(ws, { type: "mention_error", mateId: msg.mateId, error: "Mate not found" });
534
+ return;
535
+ }
496
536
  }
497
537
 
498
538
  var mateName = (mate.profile && mate.profile.displayName) || mate.name || "Mate";
@@ -601,15 +641,17 @@ function attachMateInteraction(ctx) {
601
641
  sendToSession(session.localId, { type: "mention_done", mateId: msg.mateId });
602
642
  send({ type: "mention_processing", mateId: msg.mateId, active: false });
603
643
 
604
- if (isMate) {
644
+ if (isMate && !isPlain) {
605
645
  notifyMentionResponse(session, msg.mateId, mateName, fullText);
606
646
  }
607
647
 
608
- // Check if the mate wrote a debate brief during this turn
609
- ctx.checkForDmDebateBrief(session, msg.mateId, mateCtx);
648
+ if (!isPlain) {
649
+ // Check if the mate wrote a debate brief during this turn
650
+ ctx.checkForDmDebateBrief(session, msg.mateId, mateCtx);
610
651
 
611
- // Generate session digest for mate's long-term memory
612
- digestMentionSession(session, msg.mateId, mateCtx, fullText, msg.text);
652
+ // Generate session digest for mate's long-term memory
653
+ digestMentionSession(session, msg.mateId, mateCtx, fullText, msg.text);
654
+ }
613
655
  },
614
656
  onError: function (errMsg) {
615
657
  session._mentionInProgress = false;
@@ -644,18 +686,29 @@ function attachMateInteraction(ctx) {
644
686
  delete session._mentionSessions[msg.mateId];
645
687
  }
646
688
 
647
- // Load Mate CLAUDE.md
648
- var mateDir = matesModule.getMateDir(mateCtx, msg.mateId);
649
689
  var claudeMd = "";
650
- try {
651
- claudeMd = fs.readFileSync(path.join(mateDir, "CLAUDE.md"), "utf8");
652
- } catch (e) {
653
- // CLAUDE.md may not exist for new mates
654
- }
690
+ var recentDigests = "";
691
+ if (isPlain) {
692
+ // Plain @mention: route to the vendor with just enough framing so the
693
+ // model understands it is a sidebar response, not the primary agent.
694
+ claudeMd =
695
+ "A user is working with another coding assistant and has @mentioned you for a second opinion on the current exchange.\n" +
696
+ "The recent conversation between the user and that other assistant is included as context below.\n" +
697
+ "Read it, then answer the user's question directly. Be concise and give your honest take.\n" +
698
+ "You are not driving the session, so do not run tools or take actions unless the user explicitly asks; commenting and advising is the default.";
699
+ } else {
700
+ // Load Mate CLAUDE.md
701
+ var mateDir = matesModule.getMateDir(mateCtx, msg.mateId);
702
+ try {
703
+ claudeMd = fs.readFileSync(path.join(mateDir, "CLAUDE.md"), "utf8");
704
+ } catch (e) {
705
+ // CLAUDE.md may not exist for new mates
706
+ }
655
707
 
656
- // Load session digests (unified: uses memory-summary.md if available)
657
- // Pass user's message as query for BM25 search of relevant past sessions
658
- var recentDigests = loadMateDigests(mateCtx, msg.mateId, mentionFullInput);
708
+ // Load session digests (unified: uses memory-summary.md if available)
709
+ // Pass user's message as query for BM25 search of relevant past sessions
710
+ recentDigests = loadMateDigests(mateCtx, msg.mateId, mentionFullInput);
711
+ }
659
712
 
660
713
  // Build initial mention context
661
714
  var mentionContext = buildMentionContext(userName, recentTurns) + recentDigests;
@@ -85,6 +85,23 @@ var formatters = {
85
85
  meta: { avatarMateId: data.avatarMateId || null },
86
86
  };
87
87
  },
88
+
89
+ user_mention: function (data) {
90
+ return {
91
+ type: "user_mention",
92
+ title: "@" + (data.fromName || "Someone"),
93
+ body: data.preview || "Mentioned you",
94
+ meta: {
95
+ fromUserId: data.fromUserId || null,
96
+ fromName: data.fromName || "",
97
+ fromAvatarStyle: data.fromAvatarStyle || null,
98
+ fromAvatarSeed: data.fromAvatarSeed || null,
99
+ fromAvatarColor: data.fromAvatarColor || null,
100
+ fromAvatarCustom: data.fromAvatarCustom || "",
101
+ persistent: true,
102
+ },
103
+ };
104
+ },
88
105
  };
89
106
 
90
107
  // ========================================================
@@ -93,6 +110,7 @@ var formatters = {
93
110
 
94
111
  function attachNotifications(ctx) {
95
112
  var broadcastAll = ctx.broadcastAll;
113
+ var sendToUser = ctx.sendToUser || null; // (userId, msg) -> void; iterates all project clients
96
114
  var pushModule = ctx.pushModule;
97
115
 
98
116
  var notifications = loadNotifications();
@@ -142,16 +160,24 @@ function attachNotifications(ctx) {
142
160
  sessionId: data.sessionId || null,
143
161
  mateId: data.mateId || null,
144
162
  ownerId: data.ownerId || null,
163
+ targetUserId: data.targetUserId || null,
145
164
  createdAt: Date.now(),
146
165
  meta: formatted.meta || {},
147
166
  };
148
167
  notifications.unshift(notif);
149
168
  saveNotifications();
150
- broadcastAll({
169
+ var payload = {
151
170
  type: "notification_created",
152
171
  notification: notif,
153
172
  unreadCount: getUnreadCount(),
154
- });
173
+ };
174
+ // If targeted at a specific user, deliver only to that user's connections.
175
+ // Otherwise broadcast to everyone (existing behavior).
176
+ if (notif.targetUserId && typeof sendToUser === "function") {
177
+ sendToUser(notif.targetUserId, payload);
178
+ } else {
179
+ broadcastAll(payload);
180
+ }
155
181
  return notif;
156
182
  }
157
183