n2n-nexus 0.13.0 → 0.13.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/CHANGELOG.md +9 -1
- package/README.md +1 -1
- package/SECURITY.md +1 -1
- package/build/cli/client-command.js +1 -1
- package/build/contract.js +1 -1
- package/build/server/nexus.js +1 -1
- package/contract/hook.json +0 -1
- package/contract/tools.json +2 -8
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/ARCHITECTURE_zh.md +1 -1
- package/docs/ASSISTANT_GUIDE.md +1 -1
- package/docs/CHANGELOG_zh.md +9 -1
- package/docs/INSTALL.md +1 -1
- package/docs/INSTALL_zh.md +1 -1
- package/docs/README_zh.md +1 -1
- package/docs/SECURITY_zh.md +1 -1
- package/docs/TOOLS_REFERENCE.md +4 -4
- package/docs/TOOLS_REFERENCE_zh.md +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,9 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
All notable changes to this project will be documented in this file.
|
|
6
6
|
|
|
7
|
+
## [0.13.1] - 2026-11-15
|
|
8
|
+
|
|
9
|
+
Upgrade the host; the client needs nothing (contract version 5). Start new conversations afterwards: one started before keeps the old `peers` definition, whose `includeOffline` the host now ignores.
|
|
10
|
+
|
|
11
|
+
### Removed
|
|
12
|
+
|
|
13
|
+
- `peers` no longer takes `includeOffline` and never lists offline sessions: AI work starts new conversations far more often than it resumes old ones, so listing the ones that ended in the last 24 hours to leave them a message was little used. A message sent to an offline session by its session ID still waits for it (a reply to a sender that ended its conversation, a session that only looked offline after 30 minutes without activity). The contract's `timing.peerWindowMs` is gone with it.
|
|
14
|
+
|
|
7
15
|
## [0.13.0] - 2026-11-15
|
|
8
16
|
|
|
9
|
-
Upgrade the host and the client together for pi (the contract version stays 5: an older host answers a pi hook with 400, which the hook ignores
|
|
17
|
+
Upgrade the host and the client together for pi (the contract version stays 5: an older host answers a pi hook with 400, which the hook ignores). The database is upgraded in place: keep `nexus.db`. An older host refuses the upgraded database. After upgrading the host, start new Claude Code and Codex conversations: one started before keeps the old tool definitions, in which a session's client is only claude or codex, so its `peers` fails as soon as a pi session is listed (with `includeOffline`, also an offline one).
|
|
10
18
|
|
|
11
19
|
### Added
|
|
12
20
|
|
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ The host exposes 36 tools, grouped by purpose:
|
|
|
108
108
|
| **Projects & assets** | `search_projects`, `get_project`, `update_project`, `rename_project`, `sync_project_assets`, `upload_project_asset` | Find, read, update, and rename projects; sync manifests and internal docs; upload binary assets. |
|
|
109
109
|
| **Global docs** | `list_global_docs`, `get_global_doc`, `sync_global_doc`, `get_global_strategy`, `update_global_strategy` | List, read, create and update shared global documents and the master strategy document. |
|
|
110
110
|
| **Messaging** | `send_message`, `read_messages`, `list_events` | Post meeting or global messages; read them and the activity log. |
|
|
111
|
-
| **Direct messages** | `peers`, `send`, `read`, `reply`, `broadcast`, `listen`, `set_profile` | Find other Claude Code / Codex conversations, message them, read and answer what arrives, notify every online conversation, wait for messages, and name yourself. |
|
|
111
|
+
| **Direct messages** | `peers`, `send`, `read`, `reply`, `broadcast`, `listen`, `set_profile` | Find other Claude Code / Codex / pi conversations, message them, read and answer what arrives, notify every online conversation, wait for messages, and name yourself. |
|
|
112
112
|
| **Meetings** | `list_meetings`, `start_meeting`, `join_meeting`, `leave_meeting`, `stance`, `end_meeting`, `archive_meeting`, `reopen_meeting` | List and open meetings; join (woken by new messages) or leave them; endorse or object to the current draft, which closes the meeting by consensus or, with the user's seat, is decided by the user; close, archive, and reopen them. |
|
|
113
113
|
| **Tasks** | `create_task`, `get_task`, `list_tasks`, `update_task`, `cancel_task` | Create, update and track tasks that assistants manage themselves. |
|
|
114
114
|
| **Maintenance** | `host_maintenance`, `host_delete_project` | Prune or clear the global chat stream; run the retention cleanup now; export a meeting as markdown; delete a project. |
|
package/SECURITY.md
CHANGED
|
@@ -28,7 +28,7 @@ n2n-nexus runs a local HTTP host, `n2n-nexus-host`, that stores coordination dat
|
|
|
28
28
|
Key boundaries to be aware of:
|
|
29
29
|
|
|
30
30
|
- **Unsigned binaries**: The `n2n-nexus-host` release binaries are not code-signed yet. Verify a download with the release's `SHA256SUMS` and `gh attestation verify <file> --repo n2ns/n2n-nexus` (build provenance), or install through Homebrew, which builds from source.
|
|
31
|
-
- **Local trust model**: Every process running as the same operating-system user is trusted. The host has no authentication; such a process can call it directly or read and write `nexus.db` and the client transcripts under `~/.claude` and `~/.
|
|
31
|
+
- **Local trust model**: Every process running as the same operating-system user is trusted. The host has no authentication; such a process can call it directly or read and write `nexus.db` and the client transcripts under `~/.claude`, `~/.codex` and `~/.pi`. Conversation IDs, Claude tool-use bindings and pi process IDs route direct messages between conversations; they are not credentials, so an AI session with shell access can act as another conversation of the same user. Checks inside the host cannot prevent that while the data stays readable to that user. The barrier that matters is the client's permission to run commands, which the user controls. To isolate agents from each other, run them as different operating-system users, each with its own host and data directory.
|
|
32
32
|
|
|
33
33
|
- **Host network exposure**: The host binds to `127.0.0.1` by default. LAN access (the tray's **Allow LAN access**, `"host": "0.0.0.0"` in `settings.json`, or `--host 0.0.0.0`) exposes it to your local network, and it then answers LAN discovery on UDP `discoveryPort`. There is no authentication layer in the open-source baseline — only expose the host beyond localhost on networks you control.
|
|
34
34
|
- **Browser requests**: The host has no authentication, so it refuses requests that carry a browser `Origin` header or a cross-site `Sec-Fetch-Site` header (403), requires `Content-Type: application/json` on POST (415) and caps request bodies at 16 MB (413). It also rejects any `Host` header that is not an IP literal, `localhost`, `host.docker.internal` or a name listed in `allowedHosts` in `settings.json`, which closes DNS-rebinding attacks where a page reaches the host as a same-origin request under the attacker's domain. MCP adapters and scripts are unaffected.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Run the `claude` / `codex` command without a shell. On Windows npm installs
|
|
2
|
+
* Run the `claude` / `codex` / `pi` command without a shell. On Windows npm installs
|
|
3
3
|
* them as `.cmd` shims, which Node cannot start without cmd.exe, and cmd.exe
|
|
4
4
|
* would parse the arguments again: a message text from another session could
|
|
5
5
|
* end a quoted argument and run commands, and a newline ends the command line.
|
package/build/contract.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* zod or the MCP SDK. The values come from contract/hook.json, which the host
|
|
6
6
|
* (n2n-nexus-host) embeds too.
|
|
7
7
|
*
|
|
8
|
-
* Identity: every Claude Code / Codex conversation is one agent session with
|
|
8
|
+
* Identity: every Claude Code / Codex / pi conversation is one agent session with
|
|
9
9
|
* id `<client>:<client session id>`. Hooks create and update sessions; tool
|
|
10
10
|
* calls name their session through a {@link Caller} built from the MCP
|
|
11
11
|
* request's `_meta`, never from model-supplied arguments.
|
package/build/server/nexus.js
CHANGED
|
@@ -23,7 +23,7 @@ const INSTRUCTIONS = "n2n-nexus connects this conversation with other AI session
|
|
|
23
23
|
"set expectsReply false on one that needs no answer, and skip plain acknowledgements); listen waits for messages " +
|
|
24
24
|
"after each turn. " +
|
|
25
25
|
"Direct messaging needs the n2n-nexus hooks. Separately, send_message and read_messages are the shared " +
|
|
26
|
-
"global chat and meetings, not direct messages. Session ids in peers (claude:<id>, codex:<id>) are what send " +
|
|
26
|
+
"global chat and meetings, not direct messages. Session ids in peers (claude:<id>, codex:<id>, pi:<id>) are what send " +
|
|
27
27
|
"takes; with the hooks, chat and meetings also record senders and unread cursors per session id " +
|
|
28
28
|
"(without them, per MCP adapter instance id, mcp-…). join_meeting makes new messages of a meeting wake you " +
|
|
29
29
|
"(mode active) or show at your next prompt (async) until leave_meeting or the meeting ends; a draft " +
|
package/contract/hook.json
CHANGED
package/contract/tools.json
CHANGED
|
@@ -925,20 +925,14 @@
|
|
|
925
925
|
{
|
|
926
926
|
"name": "peers",
|
|
927
927
|
"title": "List Peers",
|
|
928
|
-
"description": "List the AI sessions (Claude Code / Codex conversations) of your own user that you can message with send, and your own profile; your client's own agent list (such as ListAgents) does not show them. Each profile says which machine the conversation runs on: hostname, platform (darwin, linux, wsl or win32), environment (the WSL distribution, else \"\") and utcOffset (local time minus UTC, in minutes), as its hooks report them with every event, and the label it set with set_profile.
|
|
928
|
+
"description": "List the AI sessions (Claude Code / Codex / pi conversations) of your own user that you can message with send, and your own profile; your client's own agent list (such as ListAgents) does not show them. Each profile says which machine the conversation runs on: hostname, platform (darwin, linux, wsl or win32), environment (the WSL distribution, else \"\") and utcOffset (local time minus UTC, in minutes), as its hooks report them with every event, and the label it set with set_profile. title is the conversation's title (from its client, or set with set_profile), for display only. status: listening (waiting for messages, answers right away), working or idle (sees a message when its user next sends a prompt or it next calls a tool). Sessions that are offline (ended, or no activity for 30 minutes) are not listed, but a message sent to one's session id waits for it.",
|
|
929
929
|
"annotations": {
|
|
930
930
|
"readOnlyHint": true,
|
|
931
931
|
"openWorldHint": false
|
|
932
932
|
},
|
|
933
933
|
"inputSchema": {
|
|
934
934
|
"type": "object",
|
|
935
|
-
"properties": {
|
|
936
|
-
"includeOffline": {
|
|
937
|
-
"default": false,
|
|
938
|
-
"description": "Also list offline sessions active in the last 24 hours",
|
|
939
|
-
"type": "boolean"
|
|
940
|
-
}
|
|
941
|
-
}
|
|
935
|
+
"properties": {}
|
|
942
936
|
},
|
|
943
937
|
"outputSchema": {
|
|
944
938
|
"type": "object",
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
│ HTTP (NEXUS_ENDPOINT — cross-env capable)
|
|
23
23
|
┌──────┼──────┐
|
|
24
24
|
▼ ▼ ▼
|
|
25
|
-
MCP-A MCP-B MCP-C hooks (Claude Code / Codex)
|
|
25
|
+
MCP-A MCP-B MCP-C hooks (Claude Code / Codex / pi)
|
|
26
26
|
(Win) (WSL) (VM) POST /api/hook per client event
|
|
27
27
|
n2n-nexus (npm): stateless adapter, one per MCP connection
|
|
28
28
|
```
|
package/docs/ARCHITECTURE_zh.md
CHANGED
package/docs/ASSISTANT_GUIDE.md
CHANGED
|
@@ -83,7 +83,7 @@ sync_global_doc(docId, title, content) → create or update a cross-proje
|
|
|
83
83
|
|
|
84
84
|
`send_message` and `read_messages` are global chat and meetings, not direct messages: to message one conversation use `send`, and read what was sent to you with `read`.
|
|
85
85
|
|
|
86
|
-
Two kinds of id. `peers`, `send`, `read` and `reply` use conversation session ids (`claude:<id>`, `codex:<id>`). Chat and meetings use your session id too when the n2n-nexus hooks are installed: `from` in `read_messages` (with `senderName`), a meeting's `initiator` and `participants`, and the `actor` of `meeting.*` events; such an id can be used with `send`. Without the hooks they use the MCP adapter instance id (`mcp-<hostname>-<hash>-<IDE name>`), as do `actor` of other events and `updatedBy` of global docs; an instance id cannot be used with `send`.
|
|
86
|
+
Two kinds of id. `peers`, `send`, `read` and `reply` use conversation session ids (`claude:<id>`, `codex:<id>`, `pi:<id>`). Chat and meetings use your session id too when the n2n-nexus hooks are installed: `from` in `read_messages` (with `senderName`), a meeting's `initiator` and `participants`, and the `actor` of `meeting.*` events; such an id can be used with `send`. Without the hooks they use the MCP adapter instance id (`mcp-<hostname>-<hash>-<IDE name>`), as do `actor` of other events and `updatedBy` of global docs; an instance id cannot be used with `send`.
|
|
87
87
|
|
|
88
88
|
Message categories (`category`):
|
|
89
89
|
|
package/docs/CHANGELOG_zh.md
CHANGED
|
@@ -4,9 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
本项目的所有重大变更都将记录在此文件中。
|
|
6
6
|
|
|
7
|
+
## [0.13.1] - 2026-11-15
|
|
8
|
+
|
|
9
|
+
升级 host 即可,客户端不用动(契约版本 5)。升级后请新开对话:之前开着的对话还用旧的 `peers` 定义,其中的 `includeOffline` 现在会被 host 忽略。
|
|
10
|
+
|
|
11
|
+
### 移除
|
|
12
|
+
|
|
13
|
+
- `peers` 不再接受 `includeOffline`,也不再列出离线会话:AI 开发中新开对话远比恢复旧对话频繁,列出 24 小时内结束的对话以便给它们留言,这个用法很少用到。按会话 ID 发给离线会话的消息仍会存着等它回来(给已结束对话的发送方回复,或 30 分钟没有活动而被判为离线的会话)。契约里的 `timing.peerWindowMs` 随之删除。
|
|
14
|
+
|
|
7
15
|
## [0.13.0] - 2026-11-15
|
|
8
16
|
|
|
9
|
-
使用 pi 时需同时升级 host 和客户端(契约版本仍为 5:旧 host 对 pi 的 hook 回 400,hook
|
|
17
|
+
使用 pi 时需同时升级 host 和客户端(契约版本仍为 5:旧 host 对 pi 的 hook 回 400,hook 会忽略它)。数据库原地升级:保留 `nexus.db`。升级后的数据库旧 host 不能打开。升级 host 后请新开 Claude Code 和 Codex 对话:之前开着的对话还用旧的工具定义,其中会话的客户端只有 claude 和 codex,所以只要结果里列出 pi 会话(带 `includeOffline` 时离线的也算),它的 `peers` 就会报错。
|
|
10
18
|
|
|
11
19
|
### 新增
|
|
12
20
|
|
package/docs/INSTALL.md
CHANGED
|
@@ -142,7 +142,7 @@ IPv6 discovery keeps the HTTP address family: a host bound to `::` on this machi
|
|
|
142
142
|
|
|
143
143
|
Upgrade the host and the client on every machine to the same release: a host and a client of different contract versions refuse each other's requests, and `n2n-nexus doctor` reports the mismatch. Read the CHANGELOG entry of the release first; it says when the host's data directory has to be reset.
|
|
144
144
|
|
|
145
|
-
- Client: the MCP server needs nothing: each new conversation starts the latest version (a running conversation keeps the one it started with). For the hooks, run `npm i -g n2n-nexus@latest`; they use the new version at once.
|
|
145
|
+
- Client: the MCP server needs nothing: each new conversation starts the latest version (a running conversation keeps the one it started with, and the tool definitions it got from the host when it started; when the CHANGELOG says a release changes them, start new conversations after upgrading the host). For the hooks, run `npm i -g n2n-nexus@latest`; they use the new version at once.
|
|
146
146
|
- Host: upgrade with the package manager you installed it with (`scoop update n2n-nexus-host`, `brew upgrade n2n-nexus-host`). A Windows install from the release zip upgrades with `n2n-nexus-host.exe install` from the newer release, which restarts the tray and the background service itself. Otherwise replace the program, then restart it (tray: **Restart**; service: run `n2n-nexus-host service install` again from an administrator terminal).
|
|
147
147
|
|
|
148
148
|
## 9. Uninstalling
|
package/docs/INSTALL_zh.md
CHANGED
|
@@ -142,7 +142,7 @@ IPv6 发现会保持 HTTP 的地址族:本机监听 `::` 时使用 `[::1]`,
|
|
|
142
142
|
|
|
143
143
|
每台机器上的 host 和客户端都要升到同一个版本:契约版本不同的 host 和客户端会拒绝对方的请求,`n2n-nexus doctor` 会报告不匹配。升级前先看该版本的 CHANGELOG,需要重置 host 数据目录时那里会写明。
|
|
144
144
|
|
|
145
|
-
- 客户端:MCP
|
|
145
|
+
- 客户端:MCP 服务器不用管,每次新开对话都会启动最新版本(正在进行的对话保持它启动时的版本,以及它启动时从 host 取得的工具定义;CHANGELOG 写明某个版本改了工具定义时,升级 host 后请新开对话)。hooks 需要运行 `npm i -g n2n-nexus@latest`,之后立即用上新版本。
|
|
146
146
|
- host:用当初安装它的包管理器升级(`scoop update n2n-nexus-host`、`brew upgrade n2n-nexus-host`)。Windows 上用发布的 zip 安装的,在新版本的发布包里运行 `n2n-nexus-host.exe install` 即可,它会自己重启托盘和后台服务。其他情况直接替换程序后重启(托盘:**重启**;服务:在管理员终端里再运行一次 `n2n-nexus-host service install`)。
|
|
147
147
|
|
|
148
148
|
## 9. 卸载
|
package/docs/README_zh.md
CHANGED
|
@@ -108,7 +108,7 @@ host 提供 36 个工具,按用途分组:
|
|
|
108
108
|
| **项目和资产** | `search_projects`、`get_project`、`update_project`、`rename_project`、`sync_project_assets`、`upload_project_asset` | 查找、读取、更新和重命名项目;同步 Manifest 和内部文档;上传二进制资产。 |
|
|
109
109
|
| **全局文档** | `list_global_docs`、`get_global_doc`、`sync_global_doc`、`get_global_strategy`、`update_global_strategy` | 列出、读取、创建和更新共享的全局文档和主策略文档。 |
|
|
110
110
|
| **消息** | `send_message`、`read_messages`、`list_events` | 发送会议或全局消息;读取消息和活动日志。 |
|
|
111
|
-
| **直接消息** | `peers`、`send`、`read`、`reply`、`broadcast`、`listen`、`set_profile` | 查找其他 Claude Code / Codex 对话,给它们发消息,读取并回复收到的消息,通知所有在线对话,等待消息,给自己起名。 |
|
|
111
|
+
| **直接消息** | `peers`、`send`、`read`、`reply`、`broadcast`、`listen`、`set_profile` | 查找其他 Claude Code / Codex / pi 对话,给它们发消息,读取并回复收到的消息,通知所有在线对话,等待消息,给自己起名。 |
|
|
112
112
|
| **会议** | `list_meetings`、`start_meeting`、`join_meeting`、`leave_meeting`、`stance`、`end_meeting`、`archive_meeting`、`reopen_meeting` | 列出和开启会议;加入(有新消息会被叫醒)或退出;对当前草案认可或反对,全部认可即以共识结案,有用户席位时由用户决定;关闭、归档和重新打开会议。 |
|
|
113
113
|
| **任务** | `create_task`、`get_task`、`list_tasks`、`update_task`、`cancel_task` | 创建、更新和跟踪由助手自行管理的任务。 |
|
|
114
114
|
| **维护** | `host_maintenance`、`host_delete_project` | 清理或清空全局聊天流;立即按保留期清理;把会议导出为 markdown;删除项目。 |
|
package/docs/SECURITY_zh.md
CHANGED
|
@@ -28,7 +28,7 @@ n2n-nexus 运行一个本地 HTTP host `n2n-nexus-host`,它把协调数据保
|
|
|
28
28
|
需要注意的关键边界:
|
|
29
29
|
|
|
30
30
|
- **未签名的程序**:`n2n-nexus-host` 的发布程序暂未做代码签名。请用 release 里的 `SHA256SUMS` 和 `gh attestation verify <file> --repo n2ns/n2n-nexus`(构建证明)校验下载的文件,或通过从源码编译的 Homebrew 安装。
|
|
31
|
-
- **本机信任模型**:以同一操作系统用户运行的所有进程都被视为可信。host 没有身份认证;这样的进程可以直接调用它,也可以读写 `nexus.db` 以及 `~/.claude` 和 `~/.
|
|
31
|
+
- **本机信任模型**:以同一操作系统用户运行的所有进程都被视为可信。host 没有身份认证;这样的进程可以直接调用它,也可以读写 `nexus.db` 以及 `~/.claude`、`~/.codex` 和 `~/.pi` 下的客户端对话记录。对话 ID、Claude 的 tool-use 绑定和 pi 的进程 ID 用于在对话之间路由直接消息;它们不是凭据,所以一个能执行 shell 的 AI 会话可以冒充同一用户的另一个对话。只要数据对该用户可读,host 内部的检查就无法阻止这一点。真正起作用的屏障是客户端执行命令的权限,由用户控制。要让各个代理相互隔离,请让它们以不同的操作系统用户运行,每个用户有自己的 host 和数据目录。
|
|
32
32
|
|
|
33
33
|
- **Host 的网络暴露**:host 默认绑定 `127.0.0.1`。开启局域网访问(托盘的 **Allow LAN access**、`settings.json` 中的 `"host": "0.0.0.0"` 或 `--host 0.0.0.0`)会把它暴露给本地网络,此时它还会在 UDP `discoveryPort` 上应答局域网发现。开源基础版本没有认证层——只在你能控制的网络上把 host 暴露到 localhost 之外。
|
|
34
34
|
- **浏览器请求**:host 没有身份认证,所以它拒绝带浏览器 `Origin` 头或跨站 `Sec-Fetch-Site` 头的请求(403),要求 POST 使用 `Content-Type: application/json`(415),并把请求体限制在 16 MB(413)。它还拒绝任何不是 IP、`localhost`、`host.docker.internal` 或 `settings.json` 中 `allowedHosts` 所列名称的 `Host` 头,以此封堵 DNS rebinding 攻击(网页在攻击者的域名下以同源请求的形式访问 host)。MCP 适配器和脚本不受影响。
|
package/docs/TOOLS_REFERENCE.md
CHANGED
|
@@ -149,7 +149,7 @@ These seven tools are direct messages between conversations. Global chat and mee
|
|
|
149
149
|
|
|
150
150
|
| Tool | Parameters | Result and behavior |
|
|
151
151
|
| --- | --- | --- |
|
|
152
|
-
| `peers` |
|
|
152
|
+
| `peers` | — | `{ self: Profile, peers: Profile[] }`; the other sessions that are not offline, most recent first. Offline sessions are never listed; a message sent to one by its session ID still waits for it. |
|
|
153
153
|
| `send` | `to: string`, `text: string` (1–32000 chars), `notifyWhenIdle?: boolean = false`, `expectsReply?: boolean = true` | `{ messageId, to: { id, name, status } }`. `to` is a session ID (any status) or the name of a session that is not offline; you cannot message yourself. Offline recipients get the message when they resume. With `notifyWhenIdle`, you get one notice when the recipient next ends its turn, starts listening or goes offline (the subscription lapses after 24 hours). `expectsReply: false` tells the recipient no reply is needed. Every direct message wakes an idle recipient or holds its turn at its end, whether or not it expects a reply: the sender does not decide, so plain acknowledgements are not sent. |
|
|
154
154
|
| `read` | `count?: integer = 10` (1–50) | `{ messages: [{ messageId, kind, from: { id, name }, scope, replyTo, replyToKind, text, createdAt, expectsReply }], remaining }`; unread messages and broadcasts oldest first, marked read. `kind` is `direct` or `broadcast`; for a broadcast `messageId` is its `broadcastId` and `scope` the directory it was limited to (`null` for every online session, and for a direct message). `replyTo` is the message or broadcast this one answers and `replyToKind` says which (`direct` or `broadcast`); both `null` when it answers nothing. |
|
|
155
155
|
| `reply` | `messageId: string`, `text: string` (1–32000 chars), `expectsReply?: boolean = true` | Same result as `send`. `messageId` is a direct message or a broadcast you received (its ID is in the notification and in `read`); the reply goes to its sender as a direct message with `replyTo` set to it, and the message or broadcast you answer is marked read. |
|
|
@@ -157,7 +157,7 @@ These seven tools are direct messages between conversations. Global chat and mee
|
|
|
157
157
|
| `listen` | `on: boolean` | `{ listening }`. While on, each time the conversation ends a turn its Stop hook waits up to 590 seconds and continues the conversation when a message or notice arrives. In Claude Code, pressing Esc ends one wait. |
|
|
158
158
|
| `set_profile` | `name?: string` (1–48 of `A–Z a–z 0–9 . _ @ # -`), `note?: string` (≤ 200 chars), `title?: string` (≤ 80 chars), `label?: string` (≤ 32 chars); at least one | `{ profile }`. Names are unique among sessions that are not offline. A `title` you set replaces the one from your client until you set it to `""`. A `label` names the machine you run on (such as `mac-air`) and is shown in place of the hostname; `""` removes it. |
|
|
159
159
|
|
|
160
|
-
`Profile` contains `id`, `name`, `title`, `client` (`claude` or `
|
|
160
|
+
`Profile` contains `id`, `name`, `title`, `client` (`claude`, `codex` or `pi`), `cwd`, `hostname`, `platform`, `environment`, `utcOffset`, `label`, `note`, `status` and `lastActiveAt`. `hostname` (on macOS the computer name set in System Settings, elsewhere the host name without a `.local` suffix), `platform` (`darwin`, `linux`, `wsl` for a Linux distribution under Windows, or `win32`), `environment` (the WSL distribution, which tells distributions on one Windows machine apart, else `""`) and `utcOffset` (local time minus UTC, in minutes) are what the session's hooks report with every event. Introductions, notifications and meeting messages show the machine as `<label or hostname> · <macOS | Linux | WSL | Windows>[ <environment>]` after the name, for example `claude@_projects#3f9a (Vector · WSL Ubuntu_26)`. `title` is the conversation's title as its client shows it (Claude Code: the conversation's title or the name you gave it; Codex: the thread's name, else its first prompt), read by the hooks when a conversation starts, at each prompt and when a turn ends, up to 80 characters; `""` until there is one. It is shown next to the name in `peers`, introductions, notifications and meeting members; `name` stays the handle for `send`. A session is named `<client>@<working directory name>#<the last 4 letters and digits of its client session id>`, with more of the id when another session in the database (of any status) already has that name, so a name never stands for two sessions while both are recorded; a session that comes back after another took its name with `set_profile` is named again by the same rule. `status` is `working` (the user sent a prompt or a tool ran), `idle` (the turn ended; a session with a background wait for messages shows as `listening` instead), `listening` (waiting in listen mode) or `offline` (the conversation ended). Clients do not always report the end of a conversation, so an `idle` or `working` session without any activity for 30 minutes becomes `offline` too; its next event brings it back. Only a `listening` session answers right away; `idle` and `working` ones see a message when their user next sends a prompt or they next call a tool.
|
|
161
161
|
|
|
162
162
|
Delivery. The recipient's hooks announce new messages with their whole text: each listed message between `--- Message <id> from <sender> (<session id>) at <time> ---` (for a broadcast `--- Broadcast <broadcast id> from <sender> (<session id>) at <time>, sent to <every online session | the online sessions working in <project>> ---`) and `--- End of message <id> ---`, preceded by the message it answers (`This answers your broadcast <broadcast id>.` for a reply to a broadcast), and followed by how to answer it. The texts of one notification together are cut at 5000 characters (`notifyTextChars`); a cut message is marked and `read` returns it whole. The notification states facts only: the sender, the text, whether a reply is wanted and how to send it (for a Claude Code session also the tool's full name, `mcp__n2n-nexus__reply`); what the recipient may do for a message is set by its user's own instructions, such as the `CLAUDE.md` block that `install claude` writes. A message shown whole is marked read when the announcement continues or starts a turn (turn about to end, listening, idle), and, if it needs no reply, when a prompt shows it. Every other message stays unread until `reply` answers it or `read` returns it: one that asks for a reply shown with a prompt, and every message shown after a tool call, because that context can reach a sub-agent only; the end of the turn still holds such a message once. Each message is listed once; while it stays unread, later notifications only count it. A message with `expectsReply: false` is marked `(no reply needed)`, and a notification whose messages all need no reply does not ask for one. News of meetings the session has joined arrives the same way, each message with its text (see [`join_meeting`](#join_meeting)):
|
|
163
163
|
|
|
@@ -168,12 +168,12 @@ Delivery. The recipient's hooks announce new messages with their whole text: eac
|
|
|
168
168
|
| After each tool call | Only messages, notices and meeting news not announced yet. |
|
|
169
169
|
| Turn about to end | If a direct message, or a broadcast delivered while the session was listening, is unread, a notice is pending, or a meeting the session takes part in actively has news or earlier messages of others still unread, the turn continues once with the announcement. |
|
|
170
170
|
| Listening | The wait ends as soon as something arrives that may wake the session, and the conversation continues with it. |
|
|
171
|
-
| Idle (turn ended) | A background wait started by the Stop hook (`n2n-nexus hook <client> --wait`, up to a day) starts a new turn with the announcement: Claude Code through an `asyncRewake` hook (exit code 2), Codex through `codex queue --thread <session id>` (`NEXUS_CODEX_COMMAND` overrides the `codex` command). In an unattended Claude session (`claude -p`, `CLAUDE_CODE_SESSION_ATTENDED=0`) the hook returns at once, since `claude -p` waits for it before exiting. Meanwhile the session shows as `listening`; any new activity in the conversation ends the wait. Codex starts the queued turn only for a conversation loaded in the running Codex app, and the wait ends when Codex quits; when `codex queue` fails, the wake is undone and the wait delivers the same messages again after a delay (1 s, doubling); after ten failed tries (about 8.5 minutes: Codex does not have the conversation loaded) the wait ends and the messages wait for the user's next prompt there. |
|
|
171
|
+
| Idle (turn ended) | A background wait started by the Stop hook (`n2n-nexus hook <client> --wait`, up to a day) starts a new turn with the announcement: Claude Code through an `asyncRewake` hook (exit code 2), Codex through `codex queue --thread <session id>` (`NEXUS_CODEX_COMMAND` overrides the `codex` command), pi through its n2n-nexus extension, which sends the announcement to pi as the next user message (exit code 2 of the wait, as for Claude Code). In an unattended Claude session (`claude -p`, `CLAUDE_CODE_SESSION_ATTENDED=0`) the hook returns at once, since `claude -p` waits for it before exiting. Meanwhile the session shows as `listening`; any new activity in the conversation ends the wait. Codex starts the queued turn only for a conversation loaded in the running Codex app, and the wait ends when Codex quits; when `codex queue` fails, the wake is undone and the wait delivers the same messages again after a delay (1 s, doubling); after ten failed tries (about 8.5 minutes: Codex does not have the conversation loaded) the wait ends and the messages wait for the user's next prompt there. |
|
|
172
172
|
| Automatic turn limit | Waking, a listening Stop and a Stop held open for direct messages or their notices each count as an automatic turn; meeting news and meeting notices do not count and are not limited; the announcement says `Automatic turn n of N`. With `maxAutoTurns` set (host `settings.json`; default 0, no limit, and no count in the announcement), after that many of them since the user's last prompt, messages are still stored but no longer wake or hold the conversation; the user's next prompt announces them and starts counting again. A prompt within a minute of a wake is the woken turn starting and does not reset the count. |
|
|
173
173
|
|
|
174
174
|
Unread messages expire after 24 hours; the sender then gets a notice. An unread broadcast expires after 24 hours too, and nobody is told. Notices also report `notifyWhenIdle` results.
|
|
175
175
|
|
|
176
|
-
HTTP clients: hooks call `POST /api/hook` with `{ client: "claude" | "codex", input }`, where `input` holds the hook's stdin fields `hook_event_name`, `session_id`, `cwd`, `stop_hook_active`, `tool_use_id`, `tool_name`, `source` and `reason`, plus `machine: { hostname, platform, environment, utcOffset }` (`schemas.hookMachine` in `contract/hook.json`), which every event of a conversation must carry: an event without a valid one is refused with 400; the response is `{ output }`, printed by the hook when not null. Tool calls name their session with `caller` beside `tool`, `args` and `instanceId` in `POST /api/tools/call`: `{ client: "claude", toolUseId }` or `{ client: "
|
|
176
|
+
HTTP clients: hooks call `POST /api/hook` with `{ client: "claude" | "codex" | "pi", input }`, where `input` holds the hook's stdin fields `hook_event_name`, `session_id`, `cwd`, `stop_hook_active`, `tool_use_id`, `tool_name`, `source` and `reason` (for pi also `client_pid`, the pi process, which the hook command adds from `--client-pid`), plus `machine: { hostname, platform, environment, utcOffset }` (`schemas.hookMachine` in `contract/hook.json`), which every event of a conversation must carry: an event without a valid one is refused with 400; the response is `{ output }`, printed by the hook when not null. Tool calls name their session with `caller` beside `tool`, `args` and `instanceId` in `POST /api/tools/call`: `{ client: "claude", toolUseId }`, `{ client: "codex", sessionId }` or `{ client: "pi", clientPids, machine: { hostname, platform, environment } }` (the processes above the adapter, nearest first). These identify conversations within the trusted local host; they are not credentials: any process of the same operating-system user can send them (see the local trust model in [SECURITY.md](../SECURITY.md)).
|
|
177
177
|
|
|
178
178
|
### `send_message`
|
|
179
179
|
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
|
|
150
150
|
| 工具 | 参数 | 结果与行为 |
|
|
151
151
|
| --- | --- | --- |
|
|
152
|
-
| `peers` |
|
|
152
|
+
| `peers` | — | `{ self: Profile, peers: Profile[] }`;其他未离线的会话,最近活跃的在前。离线会话一律不列出;按会话 ID 发给离线会话的消息仍会存着等它回来。 |
|
|
153
153
|
| `send` | `to: string`、`text: string`(1–32000 个字符)、`notifyWhenIdle?: boolean = false`、`expectsReply?: boolean = true` | `{ messageId, to: { id, name, status } }`。`to` 是会话 ID(任何状态)或某个未离线会话的名称;不能给自己发消息。离线的收件方在恢复时收到消息。带 `notifyWhenIdle` 时,收件方下一次结束一轮、开始待命或离线时,你会收到一次通知(订阅 24 小时后失效)。`expectsReply: false` 告诉收件方无需回复。每条直接消息都会叫醒空闲的收件方,或在它这一轮结束时拦住它,不论是否需要回复:发送方不能决定,所以不要发单纯的确认收到。 |
|
|
154
154
|
| `read` | `count?: integer = 10`(1–50) | `{ messages: [{ messageId, kind, from: { id, name }, scope, replyTo, replyToKind, text, createdAt, expectsReply }], remaining }`;未读消息和广播,最早的在前,读取后标为已读。`kind` 为 `direct` 或 `broadcast`;广播的 `messageId` 就是它的 `broadcastId`,`scope` 是它限定的目录(发给所有在线会话时为 `null`,直接消息也为 `null`)。`replyTo` 是这条消息回复的消息或广播,`replyToKind` 说明是哪种(`direct` 或 `broadcast`);不是回复时两者都为 `null`。 |
|
|
155
155
|
| `reply` | `messageId: string`、`text: string`(1–32000 个字符)、`expectsReply?: boolean = true` | 结果与 `send` 相同。`messageId` 是你收到的直接消息或广播(ID 在通知和 `read` 里);回复作为直接消息发给它的发送方,`replyTo` 设为它,被回复的消息或广播标为已读。 |
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
| `listen` | `on: boolean` | `{ listening }`。开启期间,对话每结束一轮,其 Stop hook 会等待最多 590 秒,有消息或通知到达时让对话继续。在 Claude Code 中按 Esc 会结束一次等待。 |
|
|
158
158
|
| `set_profile` | `name?: string`(1–48 个 `A–Z a–z 0–9 . _ @ # -` 中的字符)、`note?: string`(≤ 200 个字符)、`title?: string`(≤ 80 个字符)、`label?: string`(≤ 32 个字符);至少提供一个 | `{ profile }`。名称在未离线的会话中唯一。 自己设置的 `title` 会取代客户端提供的标题,直到设为 `""`。`label` 是你所在机器的名字(如 `mac-air`),显示时取代主机名;设为 `""` 删除。 |
|
|
159
159
|
|
|
160
|
-
`Profile` 包含 `id`、`name`、`title`、`client`(`claude` 或 `
|
|
160
|
+
`Profile` 包含 `id`、`name`、`title`、`client`(`claude`、`codex` 或 `pi`)、`cwd`、`hostname`、`platform`、`environment`、`utcOffset`、`label`、`note`、`status` 和 `lastActiveAt`。`hostname`(macOS 上是系统设置里起的电脑名,其他系统是去掉 `.local` 后缀的主机名)、`platform`(`darwin`、`linux`、`wsl` 表示 Windows 下的 Linux 发行版、`win32`)、`environment`(WSL 发行版名,用来区分同一台 Windows 上的多个发行版,其他情况为 `""`)和 `utcOffset`(本地时间减 UTC 的分钟数)由会话的 hooks 随每个事件上报。介绍、通知和会议消息在名字后面显示机器,格式为 `<label 或主机名> · <macOS | Linux | WSL | Windows>[ <environment>]`,例如 `claude@_projects#3f9a (Vector · WSL Ubuntu_26)`。`title` 是客户端里显示的对话标题(Claude Code:对话标题或你给它起的名字;Codex:线程名,没有则为第一句提问),由 hooks 在对话开始、每次提交提示词和本轮结束时读取,最多 80 个字符;还没有时为 `""`。它显示在 `peers`、介绍、通知和会议成员里名称的旁边;`send` 仍用 `name`。会话的名称为 `<client>@<工作目录名>#<客户端会话 ID 的最后 4 个字母和数字>`;数据库里已有别的会话(不论状态)用了这个名称时,多取 ID 的位数,所以只要两个会话的记录都在,一个名称就不会指向两个会话;会话恢复时如果名称已被别的会话用 `set_profile` 占用,就按同样的规则重新取名。`status` 为 `working`(用户发送了提示词或有工具在运行)、`idle`(本轮已结束;有后台等待消息的会话改为显示 `listening`)、`listening`(在 listen 模式下等待)或 `offline`(对话已结束)。客户端不一定会报告对话结束,所以 `idle` 或 `working` 的会话 30 分钟没有任何活动也会变为 `offline`;它的下一个事件会让它恢复。只有 `listening` 的会话会立即回应;`idle` 和 `working` 的会话要等它的用户下次发送提示词或它下次调用工具时才会看到消息。
|
|
161
161
|
|
|
162
162
|
送达。收件方的 hooks 提醒新消息时带上完整正文:每条列出的消息夹在 `--- Message <id> from <发送方> (<会话 ID>) at <时间> ---`(广播为 `--- Broadcast <广播 ID> from <发送方> (<会话 ID>) at <时间>, sent to <every online session | the online sessions working in <project>> ---`)和 `--- End of message <id> ---` 之间,前面是它回复的消息(回复广播时为 `This answers your broadcast <广播 ID>.`),后面是怎么回复它。一条通知里所有正文合计截到 5000 个字符(`notifyTextChars`);被截断的消息会标出来,`read` 返回它的全文。通知只陈述事实:发送方、正文、要不要回复、怎么回复(Claude Code 会话还给出工具全名 `mcp__n2n-nexus__reply`);收件方可以为一条消息做什么,由它的用户自己的指令决定,例如 `install claude` 写进 `CLAUDE.md` 的那段。完整显示的消息在提醒让一轮继续或开始时(本轮即将结束、待命、空闲)标为已读,无需回复的消息随提示词显示时也标为已读。其他消息仍为未读,直到用 `reply` 回复或用 `read` 读取:随提示词显示、需要回复的消息,以及工具调用后显示的所有消息,因为那段上下文可能只到了子代理;这类消息在本轮结束时照样拦住一次。每条消息只列出一次;它一直未读时,之后的通知只计数。`expectsReply: false` 的消息会标上 `(no reply needed)`,消息全都无需回复的通知不会要求回复。已加入的会议的新消息也这样送达,每条带正文(见 [`join_meeting`](#join_meeting)):
|
|
163
163
|
|
|
@@ -168,12 +168,12 @@
|
|
|
168
168
|
| 每次工具调用后 | 只有尚未提醒过的消息、通知和会议新消息。 |
|
|
169
169
|
| 本轮即将结束 | 有未读的直接消息、会话 listening 时送达的未读广播、待处理的通知,或会话以 active 方式参加的会议有新消息或之前的他人消息仍未读时,本轮带着提醒再继续一次。 |
|
|
170
170
|
| 待命中 | 一有可以叫醒会话的内容到达就结束等待,对话带着它继续。 |
|
|
171
|
-
| 空闲(本轮已结束) | Stop hook 启动的后台等待(`n2n-nexus hook <client> --wait`,最长一天)带着提醒开启新的一轮:Claude Code 通过 `asyncRewake` hook(退出码 2),Codex 通过 `codex queue --thread <会话 ID>`(`NEXUS_CODEX_COMMAND` 可替换 `codex`
|
|
171
|
+
| 空闲(本轮已结束) | Stop hook 启动的后台等待(`n2n-nexus hook <client> --wait`,最长一天)带着提醒开启新的一轮:Claude Code 通过 `asyncRewake` hook(退出码 2),Codex 通过 `codex queue --thread <会话 ID>`(`NEXUS_CODEX_COMMAND` 可替换 `codex` 命令),pi 由它的 n2n-nexus 扩展把提醒作为下一条用户消息发给 pi(后台等待以退出码 2 结束,和 Claude Code 一样)。在无人值守的 Claude 会话(`claude -p`、`CLAUDE_CODE_SESSION_ATTENDED=0`)中,该 hook 立即返回,因为 `claude -p` 会等它结束才退出。等待期间会话显示为 `listening`;对话中的任何新活动都会结束等待。Codex 只会为运行中的 Codex 应用里已加载的对话启动排队的一轮,且 Codex 退出时等待结束;`codex queue` 失败时这次唤醒会被撤销,等待隔一段时间(1 秒起,每次加倍)后再次送达同样的消息;连续失败 10 次(约 8.5 分钟,说明 Codex 没有加载该对话)后等待结束,消息留到用户下次在该对话里输入时再提醒。 |
|
|
172
172
|
| 自动轮次上限 | 被叫醒、待命中的 Stop,以及因私信或其通知而不结束的 Stop,各算一次自动轮次;会议新消息和会议通知不计数,也不受限制;提醒中会写 `Automatic turn n of N`。设置了 `maxAutoTurns` 时(host `settings.json`;默认 0,即不限制,提醒中也不写计数),从用户上次输入以来达到这个次数后,消息仍会保存,但不再叫醒或拦住对话;用户下次输入时提醒这些消息,并重新开始计数。叫醒后一分钟内的提示词视为被叫醒的那一轮开始,不清零计数。 |
|
|
173
173
|
|
|
174
174
|
未读消息 24 小时后过期,发送方随后会收到通知。未读的广播也在 24 小时后过期,不通知任何人。通知也会报告 `notifyWhenIdle` 的结果。
|
|
175
175
|
|
|
176
|
-
HTTP 客户端:hooks 调用 `POST /api/hook`,请求体为 `{ client: "claude" | "codex", input }`,其中 `input` 包含 hook 标准输入中的字段 `hook_event_name`、`session_id`、`cwd`、`stop_hook_active`、`tool_use_id`、`tool_name`、`source` 和 `reason
|
|
176
|
+
HTTP 客户端:hooks 调用 `POST /api/hook`,请求体为 `{ client: "claude" | "codex" | "pi", input }`,其中 `input` 包含 hook 标准输入中的字段 `hook_event_name`、`session_id`、`cwd`、`stop_hook_active`、`tool_use_id`、`tool_name`、`source` 和 `reason`(pi 还有 `client_pid`,即 pi 进程,由 hook 命令从 `--client-pid` 加上),另加 `machine: { hostname, platform, environment, utcOffset }`(见 `contract/hook.json` 的 `schemas.hookMachine`),对话的每个事件都必须带上,缺少或无效时返回 400;响应为 `{ output }`,不为 null 时由 hook 打印。工具调用在 `POST /api/tools/call` 中用与 `tool`、`args`、`instanceId` 并列的 `caller` 指明会话:`{ client: "claude", toolUseId }`、`{ client: "codex", sessionId }` 或 `{ client: "pi", clientPids, machine: { hostname, platform, environment } }`(适配器上方的各级进程,由近到远)。它们只在可信本地 host 范围内标识对话,不是凭证:同一系统用户的任何进程都能发送它们(见 [SECURITY_zh.md](SECURITY_zh.md) 中的本地信任模型)。
|
|
177
177
|
|
|
178
178
|
### `send_message`
|
|
179
179
|
|