clay-server 2.35.1 → 2.36.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -87
- package/lib/project-image.js +1 -1
- package/lib/project-mate-interaction.js +72 -19
- package/lib/project-notifications.js +28 -2
- package/lib/project-user-mention.js +193 -0
- package/lib/project.js +27 -0
- package/lib/public/app.js +8 -0
- package/lib/public/modules/app-messages.js +13 -1
- package/lib/public/modules/app-notifications.js +40 -4
- package/lib/public/modules/input.js +38 -3
- package/lib/public/modules/mention.js +290 -48
- package/lib/server.js +34 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,87 +2,103 @@
|
|
|
2
2
|
<img src="media/logo/icon-full-banded-256-transparent.png" alt="Clay" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<
|
|
5
|
+
<h2 align="center">Use Claude Code and Codex in a browser, with your whole team.</h2>
|
|
6
|
+
<h4 align="center">Self-hosted on your machine. One toggle between vendors. No lock-in.</h4>
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://www.npmjs.com/package/clay-server"><img src="https://img.shields.io/npm/v/clay-server" alt="npm version" /></a>
|
|
10
|
+
<a href="https://www.npmjs.com/package/clay-server"><img src="https://img.shields.io/npm/dw/clay-server" alt="npm downloads" /></a>
|
|
11
|
+
<a href="https://github.com/chadbyte/clay"><img src="https://img.shields.io/github/stars/chadbyte/clay" alt="GitHub stars" /></a>
|
|
12
|
+
<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>
|
|
13
|
+
</p>
|
|
8
14
|
|
|
9
15
|
<p align="center"><img src="media/hero.png" alt="Clay workspace" /></p>
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
Clay is a team workspace for Claude Code and Codex, self-hosted on your machine. Onboard your team to one tool, share sessions live, switch vendors with a toggle. Your code, your Mates, your decisions, all on disk.
|
|
12
18
|
|
|
13
19
|
```bash
|
|
14
20
|
npx clay-server
|
|
15
21
|
# Scan the QR code to connect from any device
|
|
16
22
|
```
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
## Why Clay
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
**One workspace, many people.** Your whole team logs into the same workspace, not a personal editor with billing settings bolted on. Multi-user from day one, with OS-level isolation on Linux.
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
**Many projects at once.** If you bounce between repos all day, keep them all loaded in one place and run agents in parallel across them. Permission requests and completed jobs surface as notifications so nothing goes silent in a tab you forgot to check.
|
|
23
29
|
|
|
24
|
-
|
|
30
|
+
**Self-hosted.** Clay is a daemon on your machine. Your code, your sessions, your AI teammates' memory all live on disk in plain JSONL and Markdown. No proprietary database. No cloud relay. No middleman.
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
**Vendor-agnostic.** Run Claude Code and Codex in the same workspace. Toggle vendors per session. When Anthropic raises prices or OpenAI changes terms, your workflow keeps moving. Same Mates, same projects, same memory, different model.
|
|
33
|
+
|
|
34
|
+
**No lock-in.** Plain files you can grep, version, and back up. Standard cron expressions. MCP servers you already use. CLAUDE.md, AGENTS.md, .cursorrules, all loaded automatically across vendors. Walk away whenever you want, your data walks with you.
|
|
35
|
+
|
|
36
|
+
## Built for Teams
|
|
29
37
|
|
|
30
|
-
|
|
38
|
+
Clay is the workspace your team logs into, not a private editor with billing settings bolted on. Provision a server, invite your team, work in one place.
|
|
31
39
|
|
|
32
|
-
**
|
|
40
|
+
- **Multi-user on a single server.** One Clay daemon hosts everyone on your team. No per-seat SaaS, no separate installs. Add users, they log in, they're in.
|
|
41
|
+
- **OS-level isolation on Linux.** Opt in to provision each Clay user as a real Linux account. File ACLs are enforced via `setfacl`. Processes spawn under the right UID/GID. The isolation guarantees come from the OS, not from a promise in our docs.
|
|
42
|
+
- **Each member brings their own login.** Share one org-wide API key, or let each user sign in with their own Claude Code or Codex account. Costs route to whoever ran the model.
|
|
43
|
+
- **Drop into a teammate's session to help.** When someone gets stuck, jump into their project and pair in real time. Shared control, full history, no screen-share theater.
|
|
44
|
+
- **@mention a teammate when you're stuck.** Ping them right inside the session. Their notification center lights up, their phone buzzes. No teammate around? @mention a Mate instead, same gesture, same place.
|
|
45
|
+
- **Non-developers welcome.** PMs, designers, support engineers can log in to ask questions about the codebase, create issues, or read what the team built, without ever opening an editor.
|
|
33
46
|
|
|
34
|
-
|
|
47
|
+
## What it does
|
|
35
48
|
|
|
36
|
-
|
|
49
|
+
### Run Claude Code and Codex in one workspace
|
|
50
|
+
|
|
51
|
+
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.
|
|
37
52
|
|
|
38
53
|
<p align="center">
|
|
39
|
-
<img src="media/
|
|
54
|
+
<img src="media/split.gif" alt="split-screen workflow" width="700">
|
|
40
55
|
</p>
|
|
41
56
|
|
|
42
|
-
###
|
|
43
|
-
|
|
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.
|
|
57
|
+
### Every project on one dashboard
|
|
45
58
|
|
|
46
|
-
|
|
59
|
+
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.
|
|
47
60
|
|
|
48
|
-
|
|
61
|
+
### Mates: AI teammates with persistent memory
|
|
49
62
|
|
|
50
|
-
|
|
63
|
+
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.**
|
|
51
64
|
|
|
52
|
-
|
|
65
|
+
### Debate: structured multi-Mate decisions
|
|
53
66
|
|
|
54
|
-
|
|
67
|
+
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.
|
|
55
68
|
|
|
56
|
-
|
|
69
|
+
### Parallel worktrees
|
|
57
70
|
|
|
58
|
-
|
|
71
|
+
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.
|
|
59
72
|
|
|
60
|
-
|
|
73
|
+
### Ralph Loop: autonomous coding while you sleep
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
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.
|
|
63
76
|
|
|
64
|
-
|
|
77
|
+
### Web UI, mobile, push notifications
|
|
65
78
|
|
|
66
|
-
|
|
79
|
+
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.
|
|
67
80
|
|
|
68
|
-
|
|
81
|
+
<p align="center">
|
|
82
|
+
<img src="media/phone.gif" alt="Clay on phone" width="280">
|
|
83
|
+
</p>
|
|
69
84
|
|
|
70
85
|
## Who is Clay for
|
|
71
86
|
|
|
72
|
-
- **
|
|
73
|
-
- **
|
|
74
|
-
- **
|
|
87
|
+
- **Teams that want one shared workspace, not one editor each.** Onboard your whole team to a single tool, share sessions, set permissions per person, keep code on your own infrastructure.
|
|
88
|
+
- **Teams hedging vendor risk.** You want Claude today, Codex tomorrow, and the freedom to flip without rewriting your workflow.
|
|
89
|
+
- **Self-hosting developers who won't put their code in someone else's cloud.** You run the server, you own the data, you pick the model.
|
|
90
|
+
- **Codex users tired of CLI-only workflows.** Clay treats Codex as a first-class citizen, not a Claude afterthought.
|
|
91
|
+
- **Solo developers building an AI team.** Mates, Debate, and Ralph Loop give you reviewers, decision-makers, and an autonomous coding partner. Your team grows when you're ready.
|
|
75
92
|
|
|
76
93
|
## Getting Started
|
|
77
94
|
|
|
78
|
-
**Requirements:** Node.js 20
|
|
95
|
+
**Requirements:** Node.js 20+. Authenticated Claude Code CLI, Codex CLI, or both.
|
|
79
96
|
|
|
80
97
|
```bash
|
|
81
98
|
npx clay-server
|
|
82
99
|
```
|
|
83
100
|
|
|
84
|
-
On first run,
|
|
85
|
-
Open the browser URL or scan the QR code to connect from your phone instantly.
|
|
101
|
+
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
102
|
|
|
87
103
|
For remote access, use a VPN like Tailscale.
|
|
88
104
|
|
|
@@ -90,84 +106,111 @@ For remote access, use a VPN like Tailscale.
|
|
|
90
106
|
<img src="media/start.gif" alt="Clay starting from CLI" width="600">
|
|
91
107
|
</p>
|
|
92
108
|
|
|
109
|
+
## CLI Options
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx clay-server # Default (port 2633)
|
|
113
|
+
npx clay-server -p 8080 # Specify port
|
|
114
|
+
npx clay-server --yes # Skip interactive prompts (use defaults)
|
|
115
|
+
npx clay-server -y --pin 123456
|
|
116
|
+
# Non-interactive + PIN (for scripts/CI)
|
|
117
|
+
npx clay-server --add . # Add current directory to running daemon
|
|
118
|
+
npx clay-server --remove . # Remove project
|
|
119
|
+
npx clay-server --list # List registered projects
|
|
120
|
+
npx clay-server --shutdown # Stop running daemon
|
|
121
|
+
npx clay-server --dangerously-skip-permissions
|
|
122
|
+
# Bypass all permission prompts (requires PIN at setup)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Run `npx clay-server --help` for all options.
|
|
126
|
+
|
|
93
127
|
## FAQ
|
|
94
128
|
|
|
95
|
-
**"Is this
|
|
96
|
-
Clay
|
|
129
|
+
**"Is this a Claude Code wrapper?"**
|
|
130
|
+
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.
|
|
131
|
+
|
|
132
|
+
**"Can I run Claude Code and Codex in the same workspace?"**
|
|
133
|
+
Yes. Pick a vendor when you open a session. Switch per session. Same projects, same Mates, same memory.
|
|
97
134
|
|
|
98
135
|
**"Does my code leave my machine?"**
|
|
99
|
-
|
|
136
|
+
Only as model API calls (the same as using the CLI directly). Sessions, Mates, knowledge, and settings all stay on disk.
|
|
100
137
|
|
|
101
|
-
**"
|
|
102
|
-
Yes.
|
|
138
|
+
**"Does my existing CLAUDE.md / AGENTS.md / .cursorrules work?"**
|
|
139
|
+
Yes. Clay loads native instruction files for each vendor and merges the rest into the system prompt automatically.
|
|
103
140
|
|
|
104
|
-
**"
|
|
105
|
-
Yes.
|
|
141
|
+
**"Can I continue a CLI session in the browser?"**
|
|
142
|
+
Yes. CLI sessions show up in the sidebar. Browser sessions can be picked up in the CLI.
|
|
106
143
|
|
|
107
144
|
**"Does each teammate need their own API key?"**
|
|
108
|
-
No.
|
|
145
|
+
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.
|
|
146
|
+
|
|
147
|
+
**"What does OS-level isolation actually do?"**
|
|
148
|
+
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
149
|
|
|
110
150
|
**"Does it work with MCP servers?"**
|
|
111
|
-
Yes.
|
|
151
|
+
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
152
|
|
|
113
153
|
**"Can I use it on my phone?"**
|
|
114
|
-
Yes.
|
|
154
|
+
Yes. Install as a PWA on iOS or Android. Push notifications for approvals, errors, and task completion.
|
|
115
155
|
|
|
116
156
|
**"What is d.clay.studio in my browser URL?"**
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
## Why I built Clay
|
|
157
|
+
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.
|
|
120
158
|
|
|
121
|
-
|
|
159
|
+
## Our Philosophy
|
|
122
160
|
|
|
123
|
-
|
|
161
|
+
One idea: **user experience sovereignty**.
|
|
124
162
|
|
|
125
|
-
|
|
163
|
+
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.
|
|
126
164
|
|
|
127
|
-
That
|
|
165
|
+
That shows up in the technical choices we made:
|
|
128
166
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
```
|
|
167
|
+
- **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.
|
|
168
|
+
- **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.
|
|
169
|
+
- **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.
|
|
170
|
+
- **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.
|
|
144
171
|
|
|
145
|
-
|
|
172
|
+
That's the principle. The rest of the README is what it makes possible.
|
|
146
173
|
|
|
147
174
|
## Architecture
|
|
148
175
|
|
|
149
|
-
Clay drives
|
|
176
|
+
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
177
|
|
|
151
178
|
```mermaid
|
|
152
179
|
graph LR
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
180
|
+
subgraph Clients
|
|
181
|
+
B1["Browser<br/>User A"]
|
|
182
|
+
B2["Browser<br/>User B"]
|
|
183
|
+
Phone["Phone PWA<br/>+ Push"]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
subgraph Daemon["Clay Daemon (your machine)"]
|
|
187
|
+
Auth["Auth + RBAC"]
|
|
188
|
+
Server["HTTP / WS Server"]
|
|
189
|
+
Project["Project Context"]
|
|
190
|
+
YOKE["YOKE Adapter Layer"]
|
|
191
|
+
MCP["Built-in MCP servers<br/>ask-user / browser /<br/>debate / email"]
|
|
192
|
+
Push["Push (VAPID)"]
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
subgraph Vendors["Agent runtimes"]
|
|
196
|
+
Claude["Claude Agent SDK"]
|
|
197
|
+
Codex["codex app-server<br/>(JSON-RPC stdio)"]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
B1 <-->|WS| Server
|
|
201
|
+
B2 <-->|WS| Server
|
|
202
|
+
Phone <-->|WS + push| Server
|
|
203
|
+
Server --> Auth
|
|
204
|
+
Server --> Project
|
|
205
|
+
Project --> YOKE
|
|
206
|
+
Project --> MCP
|
|
207
|
+
Project --> Push
|
|
208
|
+
YOKE --> Claude
|
|
209
|
+
YOKE --> Codex
|
|
210
|
+
Push -.-> Phone
|
|
168
211
|
```
|
|
169
212
|
|
|
170
|
-
For
|
|
213
|
+
For sequence diagrams, OS-level isolation, daemon IPC, and key design decisions, see [docs/guides/architecture.md](docs/guides/architecture.md).
|
|
171
214
|
|
|
172
215
|
## Community Projects
|
|
173
216
|
|
|
@@ -195,7 +238,7 @@ If you're using Clay, let us know how in Discussions:
|
|
|
195
238
|
|
|
196
239
|
## Disclaimer
|
|
197
240
|
|
|
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.
|
|
241
|
+
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
242
|
|
|
200
243
|
## License
|
|
201
244
|
|
package/lib/project-image.js
CHANGED
|
@@ -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
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
|
|
609
|
-
|
|
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
|
-
|
|
612
|
-
|
|
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
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
//
|
|
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
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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
|
-
|
|
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
|
|