cross-agent-teams-mcp 0.5.3 → 0.5.5
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 +124 -15
- package/README.zh-CN.md +124 -15
- package/dist/cli.js +29 -7
- package/dist/cli.js.map +1 -1
- package/package.json +8 -3
- package/src/daemon/cleanup.ts +17 -1
- package/src/mcp/tools.ts +3 -1
- package/src/storage/agents-repo.ts +20 -6
package/README.md
CHANGED
|
@@ -4,12 +4,47 @@
|
|
|
4
4
|
|
|
5
5
|
A local MCP daemon that lets multiple AI coding agents (Claude Code, Codex, opencode) running on the same machine talk to each other. Agents register, send 1-to-1 messages, broadcast to a team or role, and wake each other up — all over a single daemon, no external services.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Quick start
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### Claude Code
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
```bash
|
|
12
|
+
# 1. Start the daemon (run once, keep it alive)
|
|
13
|
+
npx -y cross-agent-teams-mcp@latest daemon --port 9100 &
|
|
14
|
+
|
|
15
|
+
# 2. In your project, install the MCP config
|
|
16
|
+
npx mcpsmgr add jtianling/cross-agent-teams-mcp -a claude-code
|
|
17
|
+
|
|
18
|
+
# 3. Start Claude Code with the channel loader (manual permission prompt expected)
|
|
19
|
+
claude --dangerously-load-development-channels server:cross-agent-teams-channel
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Other agents (Codex, opencode, ...)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# 1. Start the daemon (run once, keep it alive)
|
|
26
|
+
npx -y cross-agent-teams-mcp@latest daemon --port 9100 &
|
|
27
|
+
|
|
28
|
+
# 2. In your project, install the MCP config (interactive picker)
|
|
29
|
+
npx mcpsmgr add jtianling/cross-agent-teams-mcp
|
|
30
|
+
|
|
31
|
+
# 3. Start your coding agent as usual
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Note: only Claude Code gets push wake out of the box. Codex needs the `--remote` + launcher setup (see section 2 below) for pokes; without it, it has a mailbox but no wake. opencode / cursor / other agents only receive pokes when running inside a tmux pane. If push wake isn't wired up, ask the agent to check its inbox manually ("check my xats inbox").
|
|
35
|
+
|
|
36
|
+
Then talk to your agent in plain language:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
# In agent A:
|
|
40
|
+
Register me to xats as backend on team default.
|
|
41
|
+
|
|
42
|
+
# In agent B:
|
|
43
|
+
Register me to xats as frontend on team default.
|
|
44
|
+
Send backend a message: the API has changed.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
That's it. Sections below cover the details — daemon flags, manual MCP config, codex `--remote` setup, more usage patterns.
|
|
13
48
|
|
|
14
49
|
## 1. Start the daemon
|
|
15
50
|
|
|
@@ -30,7 +65,21 @@ Common flags:
|
|
|
30
65
|
|
|
31
66
|
## 2. Configure your agent's MCP client
|
|
32
67
|
|
|
33
|
-
###
|
|
68
|
+
### Recommended: `mcpsmgr` (shown in Quick start)
|
|
69
|
+
|
|
70
|
+
[`mcpsmgr`](https://www.npmjs.com/package/mcpsmgr) reads this repo's `mcpsmgr.json` and writes the right MCP entries into your agent's config in one shot — including the Claude Code stdio channel proxy entry, the Codex `experimental_use_rmcp_client` toggle, and the streamable-http MCP entry.
|
|
71
|
+
|
|
72
|
+
To override the daemon port:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx mcpsmgr add jtianling/cross-agent-teams-mcp -a claude-code --port 9300
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Manual config
|
|
79
|
+
|
|
80
|
+
If you don't want `mcpsmgr` (private fork, custom token, custom stdio args, or you just prefer hand-edited config), the raw per-agent configs are below.
|
|
81
|
+
|
|
82
|
+
#### Claude Code (needs both entries — HTTP for tools, stdio for channel wake)
|
|
34
83
|
|
|
35
84
|
`.mcp.json` (or `~/.claude.json`):
|
|
36
85
|
|
|
@@ -64,32 +113,92 @@ claude --dangerously-load-development-channels server:cross-agent-teams-channel
|
|
|
64
113
|
|
|
65
114
|
The `server:<name>` suffix MUST equal the MCP server key in `.mcp.json` (`cross-agent-teams-channel` above). If your daemon uses `--token <t>`, add `"headers": { "Authorization": "Bearer <t>" }` to the HTTP entry.
|
|
66
115
|
|
|
67
|
-
|
|
116
|
+
#### Codex CLI
|
|
117
|
+
|
|
118
|
+
Codex talks to the daemon over Streamable HTTP. Wake-ups go through Codex's own app-server WebSocket transport — there is no channel proxy involved.
|
|
68
119
|
|
|
69
|
-
|
|
120
|
+
##### Minimum config (mailbox only, no push wake)
|
|
70
121
|
|
|
71
122
|
`~/.codex/config.toml`:
|
|
72
123
|
|
|
73
124
|
```toml
|
|
74
|
-
|
|
125
|
+
experimental_use_rmcp_client = true
|
|
126
|
+
|
|
127
|
+
[mcp_servers.cross-agent-teams-mcp]
|
|
75
128
|
type = "streamable-http"
|
|
76
129
|
url = "http://127.0.0.1:9100/mcp"
|
|
77
130
|
```
|
|
78
131
|
|
|
79
|
-
|
|
132
|
+
`experimental_use_rmcp_client = true` MUST sit at the top level — without it, streamable-http MCP servers fail to load.
|
|
133
|
+
|
|
134
|
+
(With `--token <t>` on the daemon: add `[mcp_servers.cross-agent-teams-mcp.headers]` and `Authorization = "Bearer <t>"`.)
|
|
80
135
|
|
|
81
|
-
|
|
136
|
+
In this minimum mode, `send_message` to this Codex still drops a row in its mailbox, but you have to call `get_inbox` yourself to read it — no push wake.
|
|
137
|
+
|
|
138
|
+
##### Let other agents wake you (codex-appserver poke)
|
|
139
|
+
|
|
140
|
+
To let other agents **wake** this Codex thread (not just mail it), you need `codex-appserver` delivery. The setup has one non-obvious gotcha worth calling out:
|
|
141
|
+
|
|
142
|
+
> **In `codex --remote` mode, MCP servers are loaded by the app-server, NOT by the TUI.** The MCP entry above must therefore live in the `CODEX_HOME` that the **app-server** reads at startup — usually the global `~/.codex/config.toml`. Setting `CODEX_HOME` on the TUI alone does nothing for MCP under `--remote`.
|
|
143
|
+
|
|
144
|
+
Start order:
|
|
82
145
|
|
|
83
146
|
```bash
|
|
84
|
-
codex app-server
|
|
85
|
-
codex --
|
|
147
|
+
# 1) Long-lived codex app-server somewhere (its CODEX_HOME decides the MCP set).
|
|
148
|
+
codex app-server --listen ws://127.0.0.1:8799
|
|
149
|
+
|
|
150
|
+
# 2) Codex TUI in a separate terminal, connected to the same app-server.
|
|
151
|
+
codex --remote ws://127.0.0.1:8799
|
|
86
152
|
```
|
|
87
153
|
|
|
88
|
-
|
|
154
|
+
If the app-server's `CODEX_HOME` doesn't have `cross-agent-teams-mcp` configured, the codex agent inside `--remote` won't see the MCP tools at all and `register_agent` will never fire.
|
|
155
|
+
|
|
156
|
+
##### Recommended: launcher with tmux pane auto-bind
|
|
157
|
+
|
|
158
|
+
For pokes to be injected directly into the running Codex thread (rather than landing as a tmux paste), the daemon needs to know which tmux pane the codex process lives in. The launcher pre-claims a pane via the `pre-register-codex-pane` CLI before exec'ing codex. Add to `~/.zshrc`:
|
|
159
|
+
|
|
160
|
+
```zsh
|
|
161
|
+
free-xats-codex() {
|
|
162
|
+
local xats_agent_id codex_home search_dir
|
|
163
|
+
xats_agent_id="$(uuidgen)"
|
|
164
|
+
search_dir="$PWD"
|
|
165
|
+
while [[ "$search_dir" != "/" ]]; do
|
|
166
|
+
if [[ -f "$search_dir/.codex/config.toml" ]]; then
|
|
167
|
+
codex_home="$search_dir/.codex"
|
|
168
|
+
break
|
|
169
|
+
fi
|
|
170
|
+
search_dir="${search_dir:h}"
|
|
171
|
+
done
|
|
172
|
+
|
|
173
|
+
if [[ -n "$TMUX_PANE" ]]; then
|
|
174
|
+
npx -y cross-agent-teams-mcp pre-register-codex-pane \
|
|
175
|
+
--pane "$TMUX_PANE" \
|
|
176
|
+
--agent-id "$xats_agent_id" \
|
|
177
|
+
>/dev/null 2>&1 \
|
|
178
|
+
|| echo "[xats] pre-register failed (continuing without pane claim)" >&2
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
if [[ -n "$codex_home" ]]; then
|
|
182
|
+
CODEX_HOME="$codex_home" exec codex \
|
|
183
|
+
--remote ws://127.0.0.1:8799 \
|
|
184
|
+
-c xats.agent_id="\"$xats_agent_id\"" "$@"
|
|
185
|
+
else
|
|
186
|
+
exec codex \
|
|
187
|
+
--remote ws://127.0.0.1:8799 \
|
|
188
|
+
-c xats.agent_id="\"$xats_agent_id\"" "$@"
|
|
189
|
+
fi
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
What the launcher does:
|
|
194
|
+
|
|
195
|
+
- Inside tmux (`$TMUX_PANE` set): pre-registers the pane → uuid mapping with the daemon (120s TTL). When the codex agent later calls `register_agent({agent_type: "codex", thread_id: $CODEX_THREAD_ID, ...})`, the daemon resolves `tmux_pane_id` automatically by matching the pre-reg against the codex argv.
|
|
196
|
+
- `--remote ws://127.0.0.1:8799` connects to the long-lived app-server from step (1) above.
|
|
197
|
+
- `-c xats.agent_id="\"$uuid\""` exposes the uuid in codex's argv so the daemon can verify the pane.
|
|
89
198
|
|
|
90
|
-
|
|
199
|
+
More detail (auth headers, lower-level `register_agent` form): [docs/configs/codex-cli.md](docs/configs/codex-cli.md).
|
|
91
200
|
|
|
92
|
-
|
|
201
|
+
#### Other coding agents (opencode, cursor, ...)
|
|
93
202
|
|
|
94
203
|
Anything that is not Claude Code or Codex — opencode, cursor, an editor extension, your own harness — connects over plain Streamable HTTP and registers as `agent_type="custom"` (the agent figures this out for you). There is no dedicated wake-up transport for these; cross-agent pokes are delivered by injecting text into the agent's tmux pane, so run the agent inside a tmux window and the daemon will resolve `pid → tty → pane` automatically when you register.
|
|
95
204
|
|
package/README.zh-CN.md
CHANGED
|
@@ -4,12 +4,47 @@
|
|
|
4
4
|
|
|
5
5
|
一个本地 MCP daemon, 让同一台机器上的多个 AI 编码 agent (Claude Code, Codex, opencode) 互相通信. agent 注册到 daemon, 互发 1-to-1 消息, 在 team 或 role 内广播, 互相唤醒 — 全部通过一个本地 daemon 完成, 不依赖任何外部服务.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## 快速开始
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### Claude Code
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
```bash
|
|
12
|
+
# 1. 启动 daemon (跑一次, 保持运行)
|
|
13
|
+
npx -y cross-agent-teams-mcp@latest daemon --port 9100 &
|
|
14
|
+
|
|
15
|
+
# 2. 在你的项目下安装 MCP 配置
|
|
16
|
+
npx mcpsmgr add jtianling/cross-agent-teams-mcp -a claude-code
|
|
17
|
+
|
|
18
|
+
# 3. 带上 channel loader 启动 Claude Code (需要手动确认权限)
|
|
19
|
+
claude --dangerously-load-development-channels server:cross-agent-teams-channel
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 其它 agent (Codex, opencode, ...)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# 1. 启动 daemon (跑一次, 保持运行)
|
|
26
|
+
npx -y cross-agent-teams-mcp@latest daemon --port 9100 &
|
|
27
|
+
|
|
28
|
+
# 2. 在你的项目下安装 MCP 配置 (交互式选择对应 agent)
|
|
29
|
+
npx mcpsmgr add jtianling/cross-agent-teams-mcp
|
|
30
|
+
|
|
31
|
+
# 3. 按平时的方式启动对应 coding agent
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
注意: 只有 Claude Code 默认就能收到 push 唤醒. Codex 需要 `--remote` + launcher 配置 (见下面 section 2) 才能被 poke; 没配的话只有邮箱, 不会自动醒. opencode / cursor 等其它 agent 只有跑在 tmux pane 里才能被 poke. 没接通 push 唤醒的情况下, 让 agent 自己手动收信即可 (跟它说"查一下我的 xats inbox").
|
|
35
|
+
|
|
36
|
+
之后用平时跟 agent 对话的语言就能用了:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
# Agent A 里:
|
|
40
|
+
Register me to xats as backend on team default.
|
|
41
|
+
|
|
42
|
+
# Agent B 里:
|
|
43
|
+
Register me to xats as frontend on team default.
|
|
44
|
+
Send backend a message: the API has changed.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
就这些. 下面是细节 — daemon 参数, 手动 MCP 配置, codex `--remote` 设置, 更多使用方式.
|
|
13
48
|
|
|
14
49
|
## 1. 启动 daemon
|
|
15
50
|
|
|
@@ -30,7 +65,21 @@ daemon 默认监听 `127.0.0.1:9100`. MCP endpoint: `http://127.0.0.1:9100/mcp`
|
|
|
30
65
|
|
|
31
66
|
## 2. 在 agent 端配置 MCP client
|
|
32
67
|
|
|
33
|
-
###
|
|
68
|
+
### 推荐: `mcpsmgr` (快速开始里已经演示)
|
|
69
|
+
|
|
70
|
+
[`mcpsmgr`](https://www.npmjs.com/package/mcpsmgr) 读取本仓库的 `mcpsmgr.json`, 一次性把对应 agent 需要的 MCP 条目写进配置 — 包括 Claude Code 的 stdio channel proxy 条目, Codex 的 `experimental_use_rmcp_client` 开关和 streamable-http MCP 条目.
|
|
71
|
+
|
|
72
|
+
覆盖 daemon 端口:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx mcpsmgr add jtianling/cross-agent-teams-mcp -a claude-code --port 9300
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 手动配置
|
|
79
|
+
|
|
80
|
+
如果不想用 `mcpsmgr` (私有 fork / 自定义 token / 自定义 stdio args / 或者就是想手写), 各 agent 的原始配置如下.
|
|
81
|
+
|
|
82
|
+
#### Claude Code (两个条目都需要 — HTTP 用于工具, stdio 用于 channel 唤醒)
|
|
34
83
|
|
|
35
84
|
`.mcp.json` (或 `~/.claude.json`):
|
|
36
85
|
|
|
@@ -64,32 +113,92 @@ claude --dangerously-load-development-channels server:cross-agent-teams-channel
|
|
|
64
113
|
|
|
65
114
|
`server:<name>` 后缀 **必须** 等于 `.mcp.json` 里的 MCP server key (上例中是 `cross-agent-teams-channel`). 如果 daemon 启动带了 `--token <t>`, 在 HTTP 条目里加 `"headers": { "Authorization": "Bearer <t>" }`.
|
|
66
115
|
|
|
67
|
-
|
|
116
|
+
#### Codex CLI
|
|
117
|
+
|
|
118
|
+
Codex 通过 Streamable HTTP 跟 daemon 通信. 唤醒走 Codex 自己的 app-server WebSocket, 不经 channel proxy.
|
|
68
119
|
|
|
69
|
-
|
|
120
|
+
##### 最小配置 (只能收邮箱, 没有 push 唤醒)
|
|
70
121
|
|
|
71
122
|
`~/.codex/config.toml`:
|
|
72
123
|
|
|
73
124
|
```toml
|
|
74
|
-
|
|
125
|
+
experimental_use_rmcp_client = true
|
|
126
|
+
|
|
127
|
+
[mcp_servers.cross-agent-teams-mcp]
|
|
75
128
|
type = "streamable-http"
|
|
76
129
|
url = "http://127.0.0.1:9100/mcp"
|
|
77
130
|
```
|
|
78
131
|
|
|
79
|
-
|
|
132
|
+
`experimental_use_rmcp_client = true` 必须放在**顶级**, 缺这条 streamable-http MCP 加载不了.
|
|
133
|
+
|
|
134
|
+
(daemon 带了 `--token <t>` 时, 加 `[mcp_servers.cross-agent-teams-mcp.headers]` 和 `Authorization = "Bearer <t>"`.)
|
|
80
135
|
|
|
81
|
-
|
|
136
|
+
这种最小配置下 `send_message` 给这个 codex 会写邮箱, 但需要手动调 `get_inbox` 拉读, 没有跨会话 push 唤醒.
|
|
137
|
+
|
|
138
|
+
##### 让别人能唤醒你 (codex-appserver poke)
|
|
139
|
+
|
|
140
|
+
要让别的 agent 能**主动唤醒**这个 codex thread (而不只是发邮件), 需要 `codex-appserver` delivery. 这里有个不直观的坑要写清楚:
|
|
141
|
+
|
|
142
|
+
> **`codex --remote` 模式下, MCP server 是 app-server 加载的, 不是 TUI 加载的**. 上面那段 MCP 配置必须放在 **app-server** 启动时读到的 `CODEX_HOME` 里 — 一般就是全局 `~/.codex/config.toml`. 仅在 TUI 这边设 `CODEX_HOME` 在 `--remote` 模式下对 MCP 不起作用.
|
|
143
|
+
|
|
144
|
+
启动顺序:
|
|
82
145
|
|
|
83
146
|
```bash
|
|
84
|
-
codex app-server
|
|
85
|
-
codex --
|
|
147
|
+
# 1) 在某个长跑终端起 codex app-server (它的 CODEX_HOME 决定 MCP set)
|
|
148
|
+
codex app-server --listen ws://127.0.0.1:8799
|
|
149
|
+
|
|
150
|
+
# 2) 在另一个终端启动 codex TUI, 连同一个 app-server
|
|
151
|
+
codex --remote ws://127.0.0.1:8799
|
|
86
152
|
```
|
|
87
153
|
|
|
88
|
-
|
|
154
|
+
如果第 1 步的 app-server 的 `CODEX_HOME` 里没配 `cross-agent-teams-mcp`, `--remote` 进去的 codex agent 根本看不到 MCP 工具, `register_agent` 调都调不到.
|
|
155
|
+
|
|
156
|
+
##### 推荐: launcher 函数 (tmux pane 自动绑定)
|
|
157
|
+
|
|
158
|
+
为了让 daemon 把 wake-hint 直接 inject 到 codex thread (而不是只 paste 到 tmux pane), daemon 需要知道 codex 进程在哪个 tmux pane. launcher 通过 `pre-register-codex-pane` CLI 在 exec codex 之前先把 pane 占住. 把下面的函数加到 `~/.zshrc`:
|
|
159
|
+
|
|
160
|
+
```zsh
|
|
161
|
+
free-xats-codex() {
|
|
162
|
+
local xats_agent_id codex_home search_dir
|
|
163
|
+
xats_agent_id="$(uuidgen)"
|
|
164
|
+
search_dir="$PWD"
|
|
165
|
+
while [[ "$search_dir" != "/" ]]; do
|
|
166
|
+
if [[ -f "$search_dir/.codex/config.toml" ]]; then
|
|
167
|
+
codex_home="$search_dir/.codex"
|
|
168
|
+
break
|
|
169
|
+
fi
|
|
170
|
+
search_dir="${search_dir:h}"
|
|
171
|
+
done
|
|
172
|
+
|
|
173
|
+
if [[ -n "$TMUX_PANE" ]]; then
|
|
174
|
+
npx -y cross-agent-teams-mcp pre-register-codex-pane \
|
|
175
|
+
--pane "$TMUX_PANE" \
|
|
176
|
+
--agent-id "$xats_agent_id" \
|
|
177
|
+
>/dev/null 2>&1 \
|
|
178
|
+
|| echo "[xats] pre-register failed (continuing without pane claim)" >&2
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
if [[ -n "$codex_home" ]]; then
|
|
182
|
+
CODEX_HOME="$codex_home" exec codex \
|
|
183
|
+
--remote ws://127.0.0.1:8799 \
|
|
184
|
+
-c xats.agent_id="\"$xats_agent_id\"" "$@"
|
|
185
|
+
else
|
|
186
|
+
exec codex \
|
|
187
|
+
--remote ws://127.0.0.1:8799 \
|
|
188
|
+
-c xats.agent_id="\"$xats_agent_id\"" "$@"
|
|
189
|
+
fi
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
行为:
|
|
194
|
+
|
|
195
|
+
- tmux 内 (`$TMUX_PANE` 非空): 先发一条 pre-register (pane_id + UUID + 120s TTL) 给 daemon. codex agent 之后调 `register_agent({agent_type: "codex", thread_id: $CODEX_THREAD_ID, ...})` 时, daemon 会用 pending pre-reg + 匹配 codex 进程 argv 自动绑 `tmux_pane_id`.
|
|
196
|
+
- `--remote ws://127.0.0.1:8799` 让 codex 连步骤 (1) 起好的 app-server.
|
|
197
|
+
- `-c xats.agent_id="\"$uuid\""` 把 UUID 暴露在 codex argv 里, daemon 用它反向校验 pane.
|
|
89
198
|
|
|
90
|
-
详细配置 (auth header,
|
|
199
|
+
详细配置 (auth header, 底层 `register_agent` 用法): [docs/configs/codex-cli.md](docs/configs/codex-cli.md).
|
|
91
200
|
|
|
92
|
-
|
|
201
|
+
#### 其它编码 agent (opencode, cursor, ...)
|
|
93
202
|
|
|
94
203
|
非 Claude Code 也非 Codex 的工具 — opencode, cursor, 编辑器扩展, 自己的 harness — 直接通过 Streamable HTTP 连 daemon, 注册时用 `agent_type="custom"` (agent 自己会判断). 这些 agent 没有专用的唤醒通道; 跨 agent poke 通过把文本注入到 agent 所在的 tmux pane 实现, 所以把 agent 跑在 tmux 窗口里, 注册时 daemon 会自动解析 `pid → tty → pane`.
|
|
95
204
|
|
package/dist/cli.js
CHANGED
|
@@ -388,6 +388,7 @@ function toAgentRow(row) {
|
|
|
388
388
|
var AgentsRepo = class {
|
|
389
389
|
constructor(db) {
|
|
390
390
|
this.db = db;
|
|
391
|
+
this.list = this.list.bind(this);
|
|
391
392
|
}
|
|
392
393
|
db;
|
|
393
394
|
findByIdentity(args) {
|
|
@@ -525,8 +526,8 @@ var AgentsRepo = class {
|
|
|
525
526
|
);
|
|
526
527
|
}
|
|
527
528
|
list(args) {
|
|
528
|
-
const
|
|
529
|
-
|
|
529
|
+
const exclude = args.excludeRoles ?? [];
|
|
530
|
+
const baseSelect = `SELECT
|
|
530
531
|
agent_id,
|
|
531
532
|
agent_type,
|
|
532
533
|
agent_type_name,
|
|
@@ -539,9 +540,17 @@ var AgentsRepo = class {
|
|
|
539
540
|
delivery_payload,
|
|
540
541
|
last_seen_at
|
|
541
542
|
FROM agents
|
|
542
|
-
WHERE team
|
|
543
|
-
|
|
544
|
-
|
|
543
|
+
WHERE team=?`;
|
|
544
|
+
const orderBy = ` ORDER BY registered_at ASC`;
|
|
545
|
+
let rows;
|
|
546
|
+
if (exclude.length > 0) {
|
|
547
|
+
const placeholders = exclude.map(() => "?").join(",");
|
|
548
|
+
rows = this.db.prepare(
|
|
549
|
+
`${baseSelect} AND role NOT IN (${placeholders})${orderBy}`
|
|
550
|
+
).all(args.team, ...exclude);
|
|
551
|
+
} else {
|
|
552
|
+
rows = this.db.prepare(`${baseSelect}${orderBy}`).all(args.team);
|
|
553
|
+
}
|
|
545
554
|
const nowMs = Date.now();
|
|
546
555
|
return rows.map((row) => {
|
|
547
556
|
const agent = toAgentRow(row);
|
|
@@ -3642,7 +3651,7 @@ function registerBusinessTools(server, db, getCallerAgentId, fanout, onRegisterS
|
|
|
3642
3651
|
if (typeof who !== "string") return toText(who);
|
|
3643
3652
|
const row = agents.findById(who);
|
|
3644
3653
|
return run(() => ({
|
|
3645
|
-
agents: agents.list({ team: row.team }).map(toPublicAgentRow)
|
|
3654
|
+
agents: agents.list({ team: row.team, excludeRoles: ["__channel_proxy__"] }).map(toPublicAgentRow)
|
|
3646
3655
|
}));
|
|
3647
3656
|
}
|
|
3648
3657
|
);
|
|
@@ -4249,11 +4258,24 @@ function runCleanup(db, opts = {}) {
|
|
|
4249
4258
|
);
|
|
4250
4259
|
const deleteMessages = db.prepare(`DELETE FROM messages WHERE sent_at < ?`);
|
|
4251
4260
|
const deleteEvents = db.prepare(`DELETE FROM events WHERE created_at < ?`);
|
|
4261
|
+
const deleteStaleProxies = db.prepare(
|
|
4262
|
+
`DELETE FROM agents
|
|
4263
|
+
WHERE role = '__channel_proxy__'
|
|
4264
|
+
AND last_seen_at < ?
|
|
4265
|
+
AND NOT EXISTS (
|
|
4266
|
+
SELECT 1 FROM agents host
|
|
4267
|
+
WHERE host.delivery_kind = 'claude-channel'
|
|
4268
|
+
AND host.role <> '__channel_proxy__'
|
|
4269
|
+
AND json_extract(host.delivery_payload, '$.channel_session_id')
|
|
4270
|
+
= json_extract(agents.delivery_payload, '$.channel_session_id')
|
|
4271
|
+
)`
|
|
4272
|
+
);
|
|
4252
4273
|
const tx = db.transaction(() => {
|
|
4253
4274
|
const s = deleteStatus.run(ageCutoff);
|
|
4254
4275
|
const m = deleteMessages.run(ageCutoff);
|
|
4255
4276
|
const e = deleteEvents.run(ageCutoff);
|
|
4256
|
-
|
|
4277
|
+
const p = deleteStaleProxies.run(ageCutoff);
|
|
4278
|
+
return Number(s.changes) + Number(m.changes) + Number(e.changes) + Number(p.changes);
|
|
4257
4279
|
});
|
|
4258
4280
|
return { deleted: tx() };
|
|
4259
4281
|
}
|