agent-embassy 1.9.4 → 2.0.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +28 -21
  4. package/README.zh-CN.md +26 -20
  5. package/dist/src/gateway/claude-helper-protocol.d.ts +1 -1
  6. package/dist/src/gateway/claude-helper-protocol.js +3 -2
  7. package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
  8. package/dist/src/gateway/claude-helper-supervisor.d.ts +1 -1
  9. package/dist/src/gateway/claude-helper-supervisor.js +1 -1
  10. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
  11. package/dist/src/gateway/claude-helper.js +12 -4
  12. package/dist/src/gateway/claude-helper.js.map +1 -1
  13. package/dist/src/gateway/cli-copy.d.ts +1 -1
  14. package/dist/src/gateway/cli-copy.en.d.ts +9 -4
  15. package/dist/src/gateway/cli-copy.en.js +10 -6
  16. package/dist/src/gateway/cli-copy.en.js.map +1 -1
  17. package/dist/src/gateway/cli-copy.js +5 -0
  18. package/dist/src/gateway/cli-copy.js.map +1 -1
  19. package/dist/src/gateway/cli-copy.zh-CN.d.ts +9 -4
  20. package/dist/src/gateway/cli-copy.zh-CN.js +10 -6
  21. package/dist/src/gateway/cli-copy.zh-CN.js.map +1 -1
  22. package/dist/src/gateway/cli.d.ts +3 -9
  23. package/dist/src/gateway/cli.js +56 -63
  24. package/dist/src/gateway/cli.js.map +1 -1
  25. package/dist/src/gateway/codex-local-transport.d.ts +1 -1
  26. package/dist/src/gateway/codex-local-transport.js +2 -2
  27. package/dist/src/gateway/codex-local-transport.js.map +1 -1
  28. package/dist/src/gateway/config.d.ts +4 -3
  29. package/dist/src/gateway/config.js +5 -11
  30. package/dist/src/gateway/config.js.map +1 -1
  31. package/dist/src/gateway/control.d.ts +20 -41
  32. package/dist/src/gateway/control.js +54 -96
  33. package/dist/src/gateway/control.js.map +1 -1
  34. package/dist/src/gateway/dashboard-copy.en.js +2 -2
  35. package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
  36. package/dist/src/gateway/dashboard-copy.zh-CN.js +2 -2
  37. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
  38. package/dist/src/gateway/federation-nodes.d.ts +1 -2
  39. package/dist/src/gateway/federation-nodes.js +29 -14
  40. package/dist/src/gateway/federation-nodes.js.map +1 -1
  41. package/dist/src/gateway/live-dashboard-app/app.js +2 -2
  42. package/dist/src/gateway/live-dashboard-command.d.ts +3 -1
  43. package/dist/src/gateway/live-dashboard-command.js +1 -1
  44. package/dist/src/gateway/live-dashboard-command.js.map +1 -1
  45. package/dist/src/gateway/peer-protocol.d.ts +1 -0
  46. package/dist/src/gateway/peer-protocol.js +5 -0
  47. package/dist/src/gateway/peer-protocol.js.map +1 -1
  48. package/dist/src/gateway/providers.d.ts +3 -3
  49. package/dist/src/gateway/providers.js +7 -10
  50. package/dist/src/gateway/providers.js.map +1 -1
  51. package/dist/src/gateway/server.d.ts +1 -2
  52. package/dist/src/gateway/server.js +6 -13
  53. package/dist/src/gateway/server.js.map +1 -1
  54. package/dist/src/gateway/service.d.ts +3 -1
  55. package/dist/src/gateway/service.js +93 -39
  56. package/dist/src/gateway/service.js.map +1 -1
  57. package/dist/src/gateway/store.d.ts +1 -1
  58. package/dist/src/gateway/store.js +30 -29
  59. package/dist/src/gateway/store.js.map +1 -1
  60. package/dist/src/gateway/types.d.ts +4 -4
  61. package/docs/CONFIGURATION.md +27 -21
  62. package/docs/CONFIGURATION.zh-CN.md +19 -15
  63. package/docs/DASHBOARD.md +1 -1
  64. package/docs/DASHBOARD.zh-CN.md +1 -1
  65. package/docs/DELIVERY.md +2 -2
  66. package/docs/DELIVERY.zh-CN.md +2 -2
  67. package/docs/GATEWAY-ARCHITECTURE.md +48 -58
  68. package/package.json +1 -1
  69. package/skills/embassy-peer/SKILL.md +20 -15
  70. package/dist/src/gateway/state-v2-to-v3.d.ts +0 -24
  71. package/dist/src/gateway/state-v2-to-v3.js +0 -1034
  72. package/dist/src/gateway/state-v2-to-v3.js.map +0 -1
@@ -18,31 +18,37 @@ below.
18
18
  | `EMBASSY_DELIVERY_NOTICES` | `merged` | Claude sender notice policy: `merged` keeps stalls and folds terminal diagnostics into native status; `verbose` emits both; `quiet` emits no gateway user-frame notices |
19
19
  | `EMBASSY_TRACKING_ENABLED` | `1` | Global progress-watch kill switch; set exactly `0` to reject `--track`, `--idle-minutes`, and `TRACK:` open attempts. Active watches are memory-only and end with the broker process; they are never restored after restart. With no active watch, `DONE:` is inert and `untrack` is not specially rejected—it returns `NOT_FOUND`. Any value other than `1` or `0` is a configuration error |
20
20
  | `EMBASSY_LOCALE` | `en` | CLI output language, exactly `en` or `zh-CN`. The `--lang` flag overrides it for the invocation that carries it; an unset or empty value means `en`, and any other value is an argument error |
21
- | `EMBASSY_HOSTS` | `this-mac` | Legacy host-list parser: the value must still be 1 through 32 unique lowercase aliases, but it is not federation authority. Without `nodes.json`, `serve` remains local-only as `this-mac` regardless of this value. When `nodes.json` is configured, explicitly setting this variable fails closed with `INVALID_GATEWAY_CONFIGURATION` |
21
+
22
+ Before any Embassy client call that talks to the broker, the CLI reads the state
23
+ directory and `nodes.json`, then connects to the control socket; grant a sandboxed
24
+ Codex task that directory as a writable root, or approve equivalent local access,
25
+ and do not relocate state or start a second broker to work around a denial.
22
26
 
23
27
  Federation authority comes only from `nodes.json` in `EMBASSY_STATE_DIR`. It
24
28
  must be a current-user-owned mode-0600 regular file whose exact object shape is
25
29
  `{"version":1,"host":"<lowercase-host>","nodes":["<lowercase-ssh-alias>",...]}`.
26
- `host` names this broker; `nodes` contains 1 through 31 unique OpenSSH aliases,
30
+ `host` names this broker; `nodes` contains 0 through 31 unique OpenSSH aliases,
27
31
  omits `host`, and keeps the federation at 32 total hosts or fewer. Each listed
28
- node is the fixed SSH destination for `embassy peer-stdio`. A missing file keeps
29
- the broker local-only as `this-mac`.
32
+ node is the fixed SSH destination for `embassy peer-stdio`. The file is mandatory;
33
+ when it is absent, Embassy prints the exact `nodes:[]` local-only fix and refuses startup.
34
+ Removing a peer does not remove its durable mirrors; reset private state before restarting with that peer absent.
30
35
 
31
- ### Offline state upgrade
36
+ ### Private state reset
32
37
 
33
- Schema 3 is the broker's only native state format. Before starting this release
34
- against schema-2 state, stop the broker and run:
38
+ Version 2.0 accepts only fresh schema-4 private state; it does not convert or
39
+ rewrite older state. Before the reset, use the old running broker's `status`
40
+ and delivery lookups to verify that no queued, armed, or accepted work remains
41
+ and every delivery has settled. Then:
35
42
 
36
- ```bash
37
- embassy convert-state-v2-to-v3
38
- ```
43
+ 1. Stop the broker.
44
+ 2. Move `gateway-state.json` aside so the old ledger remains recoverable.
45
+ 3. Keep `nodes.json` in place.
46
+ 4. Start the version-2 broker to create fresh state.
47
+ 5. Re-register routes, select the Claude route, and pair the intended edges.
39
48
 
40
- The command resolves the same configured state directory as `serve`; it accepts
41
- no alternate state-path argument and starts no providers, helpers, discovery,
42
- listener, or control socket. It verifies a byte-identical v2 backup before
43
- atomically installing and reading back v3 state. Normal output contains only
44
- success and the backup basename. If conversion fails, use the exact normalized
45
- safe code; do not retry a commit-unknown result or hand-edit the state file.
49
+ An old or unknown schema refuses with `GATEWAY_STATE_SCHEMA_UNSUPPORTED` and
50
+ does not mutate the state file. There is no conversion command or automatic
51
+ recovery path.
46
52
 
47
53
  The live dashboard is available directly at `http://127.0.0.1:41961/` while
48
54
  its foreground companion runs. Its port is a per-invocation CLI choice, not an
@@ -84,7 +90,7 @@ two hours.
84
90
 
85
91
  A CLI initiator receives the full `conv_` token in its result, and every routed recipient receives the same token in the inbound provenance envelope and reply hint. The token is a memory-only participant-scoped locator, not an authority credential: every `reply` rechecks caller identity, conversation membership, and the live route. The token no longer exists after a broker restart; it must likewise never be retried or reconstructed after route retirement or identity replacement.
86
92
 
87
- The public launcher remains host-local. Under the implemented allowlisted SSH federation, each broker serves one local host identity and exchanges only bounded route catalogs and destination-owned handoffs with configured Embassy nodes. `register-codex` remains a local task registration: its optional `--host <id>` and alias suffix must name that broker's local host. `--host` is also mutually exclusive with `--succeeds`, which always inherits the succeeded alias's host.
93
+ The public launcher remains host-local. Under allowlisted SSH federation, each broker serves the exact host identity attested by `nodes.json`. `register-codex` infers that host; the alias (and any `--succeeds` alias) must use the same suffix.
88
94
 
89
95
  ## Claude Code's own setting: `crossSessionInbound`
90
96
 
@@ -97,10 +103,10 @@ that decision. Configure it in Claude Code, not in Embassy.
97
103
  This is the one prerequisite you must actively toggle, and it is the most
98
104
  common first-run failure — because it fails *late*. Quickstart step 3
99
105
  (`select-claude`) prints `"accepted":true` whether or not the setting is
100
- enabled: selection only creates Embassy's own permission edge and never
101
- consults Claude's native inbound policy. The refusal appears at step 4, when
102
- the send reaches the Claude end. If registration and selection both succeeded
103
- but your first `send-to-claude` does not arrive, check `crossSessionInbound` on
106
+ enabled: selection creates no permission edge and never consults Claude's
107
+ native inbound policy. Create an explicit edge with `pair`; the refusal appears
108
+ when the send reaches the Claude end. If registration, selection, and pairing succeeded
109
+ but your first `send` does not arrive, check `crossSessionInbound` on
104
110
  the destination session before suspecting the route.
105
111
 
106
112
  ## Provider and runtime contract
@@ -14,24 +14,28 @@ Embassy 主要通过各命令启动时读取的环境变量进行配置。本文
14
14
  | `EMBASSY_DELIVERY_NOTICES` | `merged` | Claude 发送方通知策略:`merged` 保留停滞通知并把终局诊断合并到原生状态;`verbose` 同时发送两者;`quiet` 不发送任何网关用户帧通知 |
15
15
  | `EMBASSY_TRACKING_ENABLED` | `1` | 全局进度监视停用开关;精确设为 `0` 后,`--track`、`--idle-minutes` 与 `TRACK:` 开启请求会被拒绝。活跃监视只存在于内存中,并随代理进程结束;重启后绝不恢复。没有活跃监视时,`DONE:` 不产生作用;`untrack` 不会因开关而被特别拒绝,而是返回 `NOT_FOUND`。取值只能是 `1` 或 `0`,其他值均为配置错误 |
16
16
  | `EMBASSY_LOCALE` | `en` | CLI 输出语言,精确取值 `en` 或 `zh-CN`。`--lang` 标志会覆盖当次调用;未设置或为空表示 `en`,其他任何取值都是参数错误 |
17
- | `EMBASSY_HOSTS` | `this-mac` | 保留的旧版主机列表解析器:取值仍必须是 1 到 32 个唯一小写别名,但不构成联合权限。没有 `nodes.json` 时,无论此值为何,`serve` 都保持仅本地的 `this-mac`。已配置 `nodes.json` 时,显式设置此变量会以 `INVALID_GATEWAY_CONFIGURATION` 关闭失败 |
18
17
 
19
- 联合权限仅来自 `EMBASSY_STATE_DIR` 中的 `nodes.json`。它必须是当前用户所有的 mode-0600 普通文件,且对象形状必须精确为 `{"version":1,"host":"<lowercase-host>","nodes":["<lowercase-ssh-alias>",...]}`。`host` 指定当前代理;`nodes` 包含 1 到 31 个唯一的 OpenSSH 别名,不得包含 `host`,使联合总主机数不超过 32。每个列出的节点都是 `embassy peer-stdio` 的固定 SSH 目的地。文件缺失时,代理保持仅本地的 `this-mac`。
18
+ 任何与网关通信的 Embassy 客户端调用,都会先读取状态目录和 `nodes.json`,
19
+ 再连接控制套接字;请将该目录授予沙箱化 Codex 任务作为可写根目录,或批准
20
+ 等效的本地访问,并且不要为了绕过拒绝而迁移状态或启动第二个网关进程。
20
21
 
21
- ### 离线状态升级
22
+ 联合权限仅来自 `EMBASSY_STATE_DIR` 中的 `nodes.json`。它必须是当前用户所有的 mode-0600 普通文件,且对象形状必须精确为 `{"version":1,"host":"<lowercase-host>","nodes":["<lowercase-ssh-alias>",...]}`。`host` 指定当前代理;`nodes` 包含 0 到 31 个唯一的 OpenSSH 别名,不得包含 `host`,使联合总主机数不超过 32。该文件是必需的;缺失时 Embassy 会打印精确的 `nodes:[]` 仅本地修复方法并拒绝启动。
23
+ 移除节点不会删除其持久镜像;在缺少该节点的配置下重启前,请重置私有状态。
22
24
 
23
- 架构 3 是代理唯一的原生状态格式。使用本版本启动架构-2 状态之前,
24
- 请先停止代理并运行:
25
+ ### 私有状态重置
25
26
 
26
- ```bash
27
- embassy convert-state-v2-to-v3
28
- ```
27
+ 版本 2.0 只接受全新的架构-4 私有状态;它不会转换或重写旧状态。重置前,
28
+ 请使用仍在运行的旧代理的 `status` 与投递查询,确认没有排队、已授权或已
29
+ 接受的工作,并且每条投递都已结算。然后:
29
30
 
30
- 该命令使用与 `serve` 相同的配置来解析状态目录;它不接受其他状态路径
31
- 参数,也不会启动提供方、辅助进程、发现、监听器或控制套接字。它先验证
32
- 逐字节一致的 v2 备份,再原子安装并回读 v3 状态。正常输出只包含成功结果
33
- 与备份文件的基本名称。转换失败时请使用精确的规范化安全代码;提交结果
34
- 未知时不要重试,也不要手工编辑状态文件。
31
+ 1. 停止代理。
32
+ 2. 将 `gateway-state.json` 移到一旁,使旧账本仍可恢复。
33
+ 3. 原样保留 `nodes.json`。
34
+ 4. 启动版本-2 代理以创建全新状态。
35
+ 5. 重新注册路由、选择 Claude 路由,并配对所需边。
36
+
37
+ 旧版或未知架构会以 `GATEWAY_STATE_SCHEMA_UNSUPPORTED` 拒绝,且不会修改
38
+ 状态文件。不存在转换命令或自动恢复路径。
35
39
 
36
40
  当前台实时仪表盘组件运行时,可直接通过 `http://127.0.0.1:41961/` 访问。端口是单次命令的 CLI 选择,而不是环境设置;如需另一个稳定端口,请向 `embassy dashboard --live` 传入 `--port <n>`,其中整数范围为 1024 到 65535。当前台进程运行时,该 URL 最多支持四个并发实时视图(可分布在窗口、标签页或浏览器中);在其中一个关闭前,第五条流会被拒绝。端口冲突会以 `LIVE_DASHBOARD_PORT_IN_USE` 失败并提示使用 `--port`;Embassy 绝不会回退到临时或其他端口。
37
41
 
@@ -58,13 +62,13 @@ embassy convert-state-v2-to-v3
58
62
 
59
63
  初始发送方从 CLI 结果获得完整 `conv_` 令牌,接收方则从入站消息的来源封装和回复提示中获得同一个令牌。令牌是内存中的参与方范围定位符,不是权限凭据:每次 `reply` 都会重新检查调用方身份、参与关系和实时路由。代理重启后令牌不再存在;路由失效或身份替换后,也不得重试或重构旧令牌。
60
64
 
61
- 公开发布的启动器仍绑定本地主机。在已实现的 SSH 许可清单联合模式下,每个代理只服务一个本地主机身份,并仅与已配置的 Embassy 节点交换有界路由目录和由目的地负责的交接。`register-codex` 仍是本地任务注册;其可选的 `--host <id>` 与别名后缀必须指向该代理的本地主机。`--host` 仍与 `--succeeds` 互斥,后者始终继承被接替别名的主机。
65
+ 公开发布的启动器仍绑定本地主机。在 SSH 许可清单联合模式下,每个代理服务 `nodes.json` 已验证的精确主机身份。`register-codex` 会推断该主机;别名及任何 `--succeeds` 别名都必须使用相同后缀。
62
66
 
63
67
  ## Claude Code 自身的设置:`crossSessionInbound`
64
68
 
65
69
  `crossSessionInbound` 是 Claude Code 的原生跨会话消息设置:它决定一个 Claude 会话接受、挂起还是拒绝来自其他会话的消息。Embassy 需要在你选择作为 Codex→Claude 目的地的会话上启用此设置,且无法覆盖该决定。请在 Claude Code 中配置它,而不是在 Embassy 中。
66
70
 
67
- 这是你唯一必须主动开启的前置条件,也是最常见的首次运行故障——因为它**失败得很晚**。快速开始的第 3 步(`select-claude`)无论该设置是否启用都会打印 `"accepted":true`:选择只创建 Embassy 自己的权限边,从不查询 Claude 的原生入站策略。拒绝要到第 4 步、消息抵达 Claude 端时才出现。如果注册与选择都成功,但你的第一条 `send-to-claude` 没有送达,请先检查目的地会话上的 `crossSessionInbound`,再去怀疑路由。
71
+ 这是你唯一必须主动开启的前置条件,也是最常见的首次运行故障——因为它**失败得很晚**。快速开始的第 3 步(`select-claude`)无论该设置是否启用都会打印 `"accepted":true`:选择不会创建权限边,也不查询 Claude 的原生入站策略。请使用 `pair` 显式创建权限边;拒绝要到消息抵达 Claude 端时才出现。如果注册、选择与配对都成功,但第一条 `send` 没有送达,请先检查目的地会话上的 `crossSessionInbound`,再去怀疑路由。
68
72
 
69
73
  ## 提供方与运行时契约
70
74
 
package/docs/DASHBOARD.md CHANGED
@@ -81,7 +81,7 @@ snapshot via same-origin `fetch`; after each bounded action it reads
81
81
  a fresh snapshot. A snapshot observation may settle already-due delivery
82
82
  deadlines before projecting state.
83
83
 
84
- App Server generations, re-anchors, and refreshes do not appear in Activity or grant route authority. The dashboard reports only best-effort runtime facts from the bounded public snapshot. That public snapshot remains schema version 2 even though the private native store is schema 3. Overview and Routes dynamically enumerate Claude, Codex, DeepSeek, Grok Build, and shell peer even when a route or connector is absent; Deliveries filters by all five source and target providers; Diagnostics shows observed protocol/version metadata, current connector health, and the last safe code. Raw peer tokens and private mailbox receipts never enter the public model. Version metadata never changes route authority.
84
+ App Server generations, re-anchors, and refreshes do not appear in Activity or grant route authority. The dashboard reports only best-effort runtime facts from the bounded public snapshot. That public snapshot remains schema version 2 even though the private native store is schema 4. Overview and Routes dynamically enumerate Claude, Codex, DeepSeek, Grok Build, and shell peer even when a route or connector is absent; Deliveries filters by all five source and target providers; Diagnostics shows observed protocol/version metadata, current connector health, and the last safe code. Raw peer tokens and private mailbox receipts never enter the public model. Version metadata never changes route authority.
85
85
 
86
86
  The Diagnostics registry block mirrors optional bounded `registry` observations on the Claude connector row: `entriesScanned`, `parseableRecords`, monotonic `parseableRecordSeenSinceBoot`, bounded per-safe-code `rejected`, and `rejectedCodesOmitted`. It derives “Parseable required fields observed”, “Empty since broker start”, or “No parseable record since broker start”. The last warning says that no Claude registry record with parseable required fields has been observed since broker start and that, if Claude is running, its registry layout may have changed; that possible layout change therefore cannot look like a healthy empty peer list. The dashboard never exposes retained native IDs, operation-local endpoint evidence, raw registry records, or the release-owned offline support matrix, and a later attempt never replays an uncertain message body.
87
87
 
@@ -38,7 +38,7 @@ embassy dashboard --live --port 41962
38
38
 
39
39
  每个请求都会检查精确的 Host 头。导航 GET 可以缺少 Origin,但每个 POST 都要求精确的 Origin 与 `X-Embassy-Request: 1`。服务器不接受 `OPTIONS`,也不发送 CORS 头。这些检查约束浏览器的跨来源请求,但不认证本地软件。没有通用控制或提供方路由、遥测或外部资源。唯一的变更路由只接受配对、取消配对、刷新发现结果和移除具名 Codex 注册四种精确操作;每次都要求页内明确说明后果并确认,正文上限为 1 KiB,并限制为每分钟六次。移除操作只携带公开的 `codex-*` 别名;代理会删除该注册的同意边,并按其持久化写入阶段结算活动工作。任务 ID 不会进入浏览器契约。浏览器客户端仅在 `localStorage` 中保存一个显示偏好键(当前选项卡与语言)。浏览器不能创建任务、发送、回复、审批、中断、更改设置,也不能调用任意代理或提供方方法。它通过同源 `fetch` 接收快照,并在每次有限操作后重新读取最新快照。一次快照观测可能会在投射状态之前结算已到期的投递截止时间。
40
40
 
41
- App Server 代际、重新锚定与刷新不会出现在“活动”中,也不会授予路由权限。仪表盘只报告有界公开快照中的尽力而为运行时事实。即使私有原生存储使用架构 3,公开快照仍保持架构版本 2。“总览”与“路由”会动态枚举 Claude、Codex、DeepSeek、Grok Build 与 Shell 对等方,即使路由或连接器缺失也会显示;“投递”可按五种发送方与接收方提供方筛选;“诊断”显示已观察协议/版本元数据、当前连接器健康度与最近安全代码。原始 peer 令牌与私有邮箱回执绝不会进入公开模型。版本元数据绝不改变路由权限。
41
+ App Server 代际、重新锚定与刷新不会出现在“活动”中,也不会授予路由权限。仪表盘只报告有界公开快照中的尽力而为运行时事实。即使私有原生存储使用架构 4,公开快照仍保持架构版本 2。“总览”与“路由”会动态枚举 Claude、Codex、DeepSeek、Grok Build 与 Shell 对等方,即使路由或连接器缺失也会显示;“投递”可按五种发送方与接收方提供方筛选;“诊断”显示已观察协议/版本元数据、当前连接器健康度与最近安全代码。原始 peer 令牌与私有邮箱回执绝不会进入公开模型。版本元数据绝不改变路由权限。
42
42
 
43
43
  “诊断”中的注册表区块会镜像 Claude 连接器行上可选的有界 `registry` 观测:`entriesScanned`、`parseableRecords`、单调的 `parseableRecordSeenSinceBoot`、按安全代码分组且有界的 `rejected`,以及 `rejectedCodesOmitted`。它会派生“已观察到可解析必需字段”、“自代理启动以来为空”或“自代理启动以来没有可解析记录”。最后一种警告会说明:自代理启动以来从未观察到带可解析必需字段的 Claude 注册表记录;如果 Claude 正在运行,它的注册表布局可能已更改。因此,这种变化不会伪装成健康的空对等列表。仪表盘绝不暴露保留的原生 ID、逐操作端点证据、原始注册表记录或发布版自有的离线支持矩阵;之后的尝试也绝不重放结果不确定的消息正文。
44
44
 
package/docs/DELIVERY.md CHANGED
@@ -28,7 +28,7 @@ Embassy.
28
28
 
29
29
  - **Progress watches are independent evidence.** An opt-in watch may outlive an opener that expired before delivery, so a worker can remain unaware of the original assignment even while thread activity keeps the watch healthy. Owners should check the opener's `delivery-status` separately before assuming the assignment text arrived.
30
30
 
31
- - **Restarts keep clean work only.** Queued and reserved bodies persist under bounded retention and may resume once against the same logical route and consent edge. Armed or accepted work at crash settles ambiguous or unconfirmed and is never replayed. Each retained message keeps its opaque delivery token and status in the private v3 state, so the sender can continue checking that exact attempt after restart. No pending waiter, shell receipt, reply, or conversation capability survives.
31
+ - **Restarts keep clean work only.** Queued and reserved bodies persist under bounded retention and may resume once against the same logical route and consent edge. Armed or accepted work at crash settles ambiguous or unconfirmed and is never replayed. Each retained message keeps its opaque delivery token and status in the private v4 state, so the sender can continue checking that exact attempt after restart. No pending waiter, shell receipt, reply, or conversation capability survives.
32
32
 
33
33
  Accepted messages are tracked toward terminal delivery while the broker and provider connections remain healthy. The dashboard distinguishes acceptance, progress, delivery, expiry, failure, ambiguity, and abandonment.
34
34
 
@@ -76,7 +76,7 @@ classified as an ambiguous write or replayed.
76
76
 
77
77
  ## Delivery tokens
78
78
 
79
- Every accepted `send-to-claude`, `send-to-codex`, and `reply` returns a delivery token: `dlv_` followed by exactly 24 base64url characters. It addresses one bounded private v3 message/status row and is not a provider receipt handle. The token is persisted only in the mode-0600 broker state; it never enters a public snapshot, normal log, provider receipt, or dashboard.
79
+ Every accepted `send` and `reply` returns a delivery token: `dlv_` followed by exactly 24 base64url characters. It addresses one bounded private v4 message/status row and is not a provider receipt handle. The token is persisted only in the mode-0600 broker state; it never enters a public snapshot, normal log, provider receipt, or dashboard.
80
80
 
81
81
  ```bash
82
82
  embassy delivery-status --token dlv_<token>
@@ -23,7 +23,7 @@ Embassy 跟踪每条被接受的消息,从 CLI 接受到终态结算的全过
23
23
 
24
24
  - **进度监视是独立证据。** 选择启用的监视可能比一条在投递前已过期的开启消息存续更久,因此即使线程活动让监视保持健康,工作方仍可能从未看到原始任务。所有者在假定任务文本已到达之前,应单独检查开启消息的 `delivery-status`。
25
25
 
26
- - **重启仅保留干净工作。** 排队或已保留的消息体按有界策略持久化,并可在同一逻辑路由和同意边上恢复一次。崩溃时已授权或已接受的工作结算为 ambiguous 或 unconfirmed,绝不重放。每条仍保留的消息会在私有 v3 状态中保存其不透明投递令牌和状态,因此发送方可在重启后继续检查这一次精确尝试。待处理的等待者、shell 回执、回复或对话能力均不保留。
26
+ - **重启仅保留干净工作。** 排队或已保留的消息体按有界策略持久化,并可在同一逻辑路由和同意边上恢复一次。崩溃时已授权或已接受的工作结算为 ambiguous 或 unconfirmed,绝不重放。每条仍保留的消息会在私有 v4 状态中保存其不透明投递令牌和状态,因此发送方可在重启后继续检查这一次精确尝试。待处理的等待者、shell 回执、回复或对话能力均不保留。
27
27
 
28
28
  已接受的消息在代理和提供方连接保持健康的情况下被跟踪至终态投递。仪表盘区分接受、进行中、已投递、过期、失败、不明确和废弃等状态。
29
29
 
@@ -45,7 +45,7 @@ Embassy 的存储、队列、`STEER:` 分类、去重、速率限制和 16 KiB
45
45
 
46
46
  ## 投递令牌
47
47
 
48
- 每次被接受的 `send-to-claude`、`send-to-codex` 和 `reply` 都会返回一个投递令牌:`dlv_` 后跟恰好 24 个 base64url 字符。它指向私有 v3 状态中一条有界的消息/状态记录,不是提供方回执句柄。令牌只持久化在 mode-0600 的代理状态中,绝不会进入公开快照、普通日志、提供方回执或任何仪表盘。
48
+ 每次被接受的 `send` 和 `reply` 都会返回一个投递令牌:`dlv_` 后跟恰好 24 个 base64url 字符。它指向私有 v4 状态中一条有界的消息/状态记录,不是提供方回执句柄。令牌只持久化在 mode-0600 的代理状态中,绝不会进入公开快照、普通日志、提供方回执或任何仪表盘。
49
49
 
50
50
  ```bash
51
51
  embassy delivery-status --token dlv_<token>
@@ -26,7 +26,7 @@ from a session that already holds a pair edge to the addressed Codex task;
26
26
  `embassy serve --inbound open` is the explicit opt-out that restores any exact
27
27
  compatible live same-UID session as an inbound sender without making it
28
28
  outbound-selected. Outbound Codex-to-Claude sends likewise require the pair
29
- edge, created by `pair` or the one-task `select-claude` shorthand. It provides
29
+ edge created by explicit `pair`; Claude selection alone creates no edge. It provides
30
30
  a single private operational view across the two products without rebuilding
31
31
  either agent runtime.
32
32
 
@@ -170,7 +170,7 @@ The status below is intentionally narrower than the target architecture.
170
170
 
171
171
  | Component | Current evidence |
172
172
  | --- | --- |
173
- | Neutral gateway types, native-v3 metadata store, bounded attempt state machine, queues, dedupe, rate limits, and public projection | **Implemented**, deterministic tests; message bodies persist under bounded retention |
173
+ | Neutral gateway types, private-v4 metadata store, bounded attempt state machine, queues, dedupe, rate limits, and public projection | **Implemented**, deterministic tests; message bodies persist under bounded retention |
174
174
  | Private JSONL control protocol over a controller-owned UDS | **Implemented**, deterministic synthetic tests; no provider connection required |
175
175
  | Static metadata-only dashboard renderer and atomic publisher | **Implemented**, deterministic security tests; the static renderer requires no browser or HTTP server |
176
176
  | Opt-in live dashboard companion (`embassy dashboard --live`) | **Implemented**, deterministic tests over the stable loopback listener, direct multi-browser access, projection, request guards, and four bounded route actions; it is a separate foreground process, never part of `embassy serve` |
@@ -181,7 +181,7 @@ The status below is intentionally narrower than the target architecture.
181
181
  | Local provider adapters and Embassy-node federation | **Implemented**, focused synthetic tests cover Claude discovery, exact Codex ownership, lazy ACP-backed DeepSeek and Grok routes with provider-local degradation and cleanup, plus bounded catalog reconciliation and destination-owned handoff over the fixed attach-only SSH transport |
182
182
  | Universal shell peer mailbox | **Implemented**, alias-plus-token same-UID attribution, hash-only durable ownership, bounded long polling, stdout-flush receipts, and restart uncertainty tests; no PID binding, token file, Keychain entry, or daemon |
183
183
  | Gateway service composition | **Implemented**, including private control-server startup, synthetic cross-provider selection/dispatch/reply correlation, metadata-only publication, and restart attempt-phase tests |
184
- | Delivery receipt/status lifecycle | **Implemented**, deterministic synthetic tests cover stable-UUID native receipt re-resolution, the merged/verbose/quiet Claude notice policy, one bounded stall notice with pending age where enabled, opaque private-v3 correlation handles, restart continuity, the closed status/terminal schema, and one-shot/bounded-wait CLI behavior |
184
+ | Delivery receipt/status lifecycle | **Implemented**, deterministic synthetic tests cover stable-UUID native receipt re-resolution, the merged/verbose/quiet Claude notice policy, one bounded stall notice with pending age where enabled, opaque private-v4 correlation handles, restart continuity, the closed status/terminal schema, and one-shot/bounded-wait CLI behavior |
185
185
  | Broker-owned cross-provider provenance framing | **Implemented**, deterministic tests cover exact Codex and Claude wire shapes, bounded long-alias attribution, recipient reply hints, reserved-tag neutralization, single wrapping across clean retries, and pre-write failure |
186
186
  | Operator/agent client CLI and package binary | **Implemented**, deterministic private-UDS tests cover the closed command family, inherited provider identity, bounded stdin-only bodies, normalized output, and ambiguous no-retry behavior |
187
187
  | Repo-shipped cross-provider skill | **Implemented** as a repo-scoped workflow over the client CLI; it is not installed into either provider's global configuration |
@@ -268,8 +268,11 @@ lookup aliases; it never changes the selected UUID. Immediately before a
268
268
  Claude-bound write, Embassy performs a fresh bounded registry scan, resolves
269
269
  that byte-identical UUID exactly once, and revalidates its current workspace,
270
270
  process, socket, and used-artifact generation. An incomplete scan, duplicate
271
- name or UUID, changed UUID, or unsafe current coordinate fails that operation
272
- closed. A name alone never restores or retargets a durable selection.
271
+ UUID, changed UUID, or unsafe current coordinate fails that operation closed.
272
+ A duplicate display name is fenced from listing, selection, and pair creation;
273
+ a pre-bound route retains its identity-pinned binding, and an operator-supplied
274
+ UUID remains the recovery selector. A name alone never restores or retargets a
275
+ durable selection.
273
276
 
274
277
  The dashboard is the single pane for the human. It shows both sanitized
275
278
  available/selected Claude aliases and explicitly registered Codex aliases,
@@ -484,12 +487,12 @@ hatch.
484
487
 
485
488
  ### Delivery status and bounded waits
486
489
 
487
- Every accepted control-plane `send_to_claude`, `send_to_codex`, or `reply`
490
+ Every accepted control-plane `send` or `reply`
488
491
  result contains both its conversation ID and a fresh opaque delivery
489
492
  correlation handle called a delivery token.
490
493
  The token has the closed form `dlv_` followed by exactly 24 base64url
491
494
  characters (`A-Z`, `a-z`, `0-9`, `_`, or `-`). It addresses one bounded
492
- private-v3 message/status row and is not a provider receipt handle or a
495
+ private-v4 message/status row and is not a provider receipt handle or a
493
496
  provider native identifier. It is stored only in the mode-0600 broker state
494
497
  and never appears in a public snapshot, normal log, provider receipt, or
495
498
  dashboard.
@@ -559,7 +562,7 @@ controller-owned mode-0700 state directory. The socket and state files are
559
562
  mode 0600. Frames are size-bounded and closed against unknown keys, methods,
560
563
  versions, and enum values.
561
564
 
562
- The closed version 1 method family is exactly these sixteen methods:
565
+ The closed version 2 method family is exactly these twenty-one methods:
563
566
 
564
567
  - `health` and `list_snapshot`, a safe public snapshot;
565
568
  - `observe_snapshot`, a read-only projection that may settle already-due
@@ -571,34 +574,34 @@ The closed version 1 method family is exactly these sixteen methods:
571
574
  available-peer inventory;
572
575
  - `pair` and `unpair`, the two-endpoint permission edge;
573
576
  - `delivery_status`, a lookup by an opaque correlation handle retained only in
574
- bounded private v3 state;
577
+ bounded private v4 state;
575
578
  - `untrack`, which closes one active progress watch by conversation token;
576
- - `send_to_claude` and `send_to_codex`, the provider-specific sends;
577
- - `reply`, the correlated reply operation; and
578
- - `refresh_dashboard`, which refreshes provider discovery and republishes.
579
+ - `send`, whose direction is derived from the resolved endpoint providers;
580
+ - `reply`, the correlated reply operation;
581
+ - `refresh_dashboard`, which refreshes provider discovery and republishes;
582
+ - `peer_catalog` and `peer_handoff`, the private federation catalog and
583
+ destination-owned handoff operations; and
584
+ - `register_peer`, `unregister_peer`, `await_peer`, and `peer_receipt`, the
585
+ shell-peer registration, mailbox, and flush-before-receipt operations.
579
586
 
580
587
  The live dashboard companion calls `observe_snapshot` for every read; its
581
588
  mutation route additionally calls `pair`, `unpair`,
582
589
  `remove_codex_registration`, and `refresh_dashboard`, and nothing else.
583
590
 
584
591
  The installed binary is `embassy`, and it is the only installed binary. Its
585
- eighteen implemented commands are
586
- `serve`, `health`, `status`, `delivery-status`, `wait-delivery`, `untrack`,
592
+ twenty-one implemented commands are
593
+ `serve`, `health`, `status`, `doctor`, `delivery-status`, `wait-delivery`, `untrack`,
587
594
  `refresh-dashboard`, `dashboard`, `register-codex`, `unregister-codex`,
588
- `select-claude`, `unselect-claude`, `pair`, `unpair`, `send-to-claude`,
589
- `send-to-codex`, `reply`, and `convert-state-v2-to-v3`. `dashboard` requires `--live` and accepts an
595
+ `select-claude`, `unselect-claude`, `pair`, `unpair`, `send`,
596
+ `reply`, `register-peer`, `unregister-peer`, `await`, and
597
+ `peer-stdio`. `dashboard` requires `--live` and accepts an
590
598
  optional `--lang en|zh-CN` and `--port <n>`; it starts the companion process
591
599
  rather than issuing a single control request. Message bodies are non-empty
592
600
  UTF-8 from standard input only, with a 16 KiB ceiling; they are never accepted
593
601
  in an argument or file. The client emits one bounded normalized JSON line and
594
602
  never returns a thread ID, provider-native ID, path, address, or message body.
595
603
  These commands require the foreground broker, except that `serve` starts it in
596
- the current terminal and `convert-state-v2-to-v3` is an offline one-shot
597
- upgrade. The converter uses only the normally resolved state directory,
598
- requires the controller lock, installs strict native schema 3 through an
599
- fsynced atomic replacement, and reports only success plus the verified v2
600
- backup basename. It starts no provider, helper, listener, discovery, or control
601
- socket. The launcher never daemonizes itself.
604
+ the current terminal. The launcher never daemonizes itself.
602
605
 
603
606
  `register-codex --alias <new> --succeeds <current>` is one atomic logical-route
604
607
  transaction. It verifies the inherited identity of the replacement task,
@@ -609,28 +612,26 @@ manual-recovery state.
609
612
 
610
613
  `select-claude --alias <current-name@host>` and
611
614
  `select-claude --session <uuid>` select the same logical session.
612
- `send-to-claude --to` accepts either form only after explicit selection. UUID
615
+ `send --to` accepts either form only after explicit selection. UUID
613
616
  input is normalized to lowercase. No command returns the
614
617
  UUID, and no historical name remains routable after a rename.
615
618
 
616
- Provider-authorized mutations require one exclusive inherited principal.
619
+ Provider-authorized registration, send, and reply operations require one exclusive inherited principal.
617
620
  Codex registration, unregister, and Codex-to-Claude send require only a valid
618
621
  `CODEX_THREAD_ID`; they fail if a non-empty Claude messaging socket is also
619
622
  inherited. Claude-to-Codex send requires only the raw inherited Claude socket
620
623
  path and fails if a non-empty Codex thread ID is also present. `reply` likewise
621
- fails with both identities or neither. `pair` and `unpair` also require an
622
- exclusive inherited `CODEX_THREAD_ID`: naming both endpoints does not make them
623
- operator commands, and they fail `CODEX_IDENTITY_REQUIRED` from a plain terminal
624
- or `CALLER_IDENTITY_CONFLICT` from inside a Claude session.
625
-
626
- `select-claude` and `unselect-claude` are the operator-runnable shorthand.
627
- They do not ignore provider identity they consume an inherited
628
- `CODEX_THREAD_ID` when one is present, to resolve the Codex end, and reject a
629
- malformed one but they tolerate its absence and then resolve the Codex end
630
- from the sole registered task. Selection is not a weaker operation than
631
- `pair`: it atomically creates the same permission edge. The health, status,
632
- dashboard refresh, live dashboard, untrack, and serve commands are the ones
633
- that genuinely ignore provider identities.
624
+ fails with both identities or neither.
625
+
626
+ `pair`, `unpair`, `select-claude`, and `unselect-claude` are control-plane
627
+ operations authorized by access to the same-UID private socket; they do not
628
+ attest inherited provider identity. Pair and unpair mutate only the exact two
629
+ named endpoints, while selection installs or removes one Claude route and
630
+ creates no consent edge. Paired mode still rechecks exact edge membership at
631
+ delivery. Removing the selected route also removes its incident consent edges
632
+ and settles their in-flight work from the durable attempt phase. Agents are
633
+ instructed to create or remove only user-chosen edges; that is an operating
634
+ norm, not an additional gateway identity check.
634
635
 
635
636
  The foreground command is:
636
637
 
@@ -668,7 +669,7 @@ does not publish candidates, select or connect to a peer, write a provider
668
669
  socket, request provider history, start a model turn, or contact a remote host.
669
670
  Validated target bindings may retain private native and socket-derived evidence
670
671
  memory-only until rescan or close, but none enters public state or persistence.
671
- Its ready result reports local host `this-mac`, dashboard filename
672
+ Its ready result reports the exact local host from `nodes.json`, dashboard filename
672
673
  `gateway-dashboard.html`, and `codexMode: "native_messaging"` without exposing
673
674
  paths.
674
675
 
@@ -682,7 +683,7 @@ thread/session generation, source alias, bounds, and conversation state.
682
683
 
683
684
  ### Progress watches
684
685
 
685
- `send-to-claude`, `send-to-codex`, and `reply` each accept an opt-in `--track`
686
+ `send` and `reply` each accept an opt-in `--track`
686
687
  flag that opens one progress watch over the resulting conversation, plus an
687
688
  optional `--idle-minutes <n>` that sets how long the watched thread may sit idle
688
689
  before each bounded liveness nudge. If the watch ultimately times out, Embassy
@@ -705,17 +706,7 @@ may outlive an opener whose own delivery expired, so check the opener's
705
706
 
706
707
  ## Codex connectors and remote hosts
707
708
 
708
- In the target multi-host design, each allowlisted execution host has a separate
709
- connector because each host has its own App Server and native state:
710
-
711
- - `this-mac`: the managed local App Server shared with Desktop;
712
- - `build-mac`: a host-local remote App Server reached through an attach-only SSH
713
- proxy; and
714
- - `lab-mac.example`: the same design, still unprobed and disabled by default.
715
-
716
- The shipped foreground launcher accepts only `this-mac`; it rejects any remote
717
- host configuration. The two SSH connectors above remain planned rather than
718
- runnable v1 routes.
709
+ Each broker's mandatory `nodes.json` gives its local connector an explicit host identity; `this-mac` has no reserved meaning. Configured peers exchange only body-free local catalogs and destination-owned handoffs over fixed SSH.
719
710
 
720
711
  The local connector resolves the managed standalone Codex release by exact
721
712
  owned path; it does not use `PATH`. That installation is separate from
@@ -927,7 +918,7 @@ the static pair, which is always written in both languages.
927
918
 
928
919
  The private store may retain:
929
920
 
930
- - schema-3 logical registrations with aliases, registration IDs, and exact
921
+ - schema-4 logical registrations with aliases, registration IDs, and exact
931
922
  provider-native route handles inside the closed private binding schema;
932
923
  - consent edges tied to exact registration IDs, so alias reuse cannot inherit
933
924
  permission;
@@ -951,13 +942,12 @@ may resume once only after their exact registration and consent authority is
951
942
  rechecked; armed and accepted work settles without replay. Callback, native
952
943
  receipt, conversation, and reply capabilities are not reconstructed.
953
944
 
954
- Private schema 3 is the binary's only native store format; the bounded public
955
- snapshot deliberately remains schema version 2. Upgrading an existing schema-2
956
- state requires the offline `embassy convert-state-v2-to-v3` command before
957
- starting the broker. It verifies and retains a byte-identical v2 backup, then
958
- atomically installs and reads back strict v3 state. The runtime performs no
959
- in-binary migration or best-effort rewrite; any non-v3 state produces the
960
- ordinary strict corrupt-state error.
945
+ Private schema 4 is the binary's only native store format; the bounded public
946
+ snapshot deliberately remains schema version 2. The runtime performs no
947
+ migration or best-effort rewrite. An old or unknown private schema refuses with
948
+ `GATEWAY_STATE_SCHEMA_UNSUPPORTED` without mutating the state file; the operator
949
+ must follow the reset-only runbook in `docs/CONFIGURATION.md`. A malformed
950
+ schema-4 document produces the ordinary strict corrupt-state error.
961
951
 
962
952
  ## Minimum filesystem and process access
963
953
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-embassy",
3
- "version": "1.9.4",
3
+ "version": "2.0.1",
4
4
  "description": "A local gateway for bidirectional messaging between Claude Code sessions and Codex tasks.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,35 +1,40 @@
1
1
  ---
2
2
  name: embassy-peer
3
- description: Operate Embassy through current name@host or Claude session-UUID selectors and universal peer-* shell routes. Use when an agent needs to register for inbound messaging, await shell-peer mail, list available peers, refresh the operator's static dashboard, manage a Codex-attested pair, send or reply under its own principal, or unregister without exposing provider credentials, socket paths, or message bodies.
3
+ description: Operate Embassy through current name@host or Claude session-UUID selectors and universal peer-* shell routes. Use when an agent needs to register for inbound messaging, await shell-peer mail, list available peers, refresh the operator's static dashboard, manage a user-chosen pair, send or reply under its own principal, or unregister without exposing provider credentials, socket paths, or message bodies.
4
4
  ---
5
5
 
6
6
  # Embassy Peer Gateway
7
7
 
8
8
  Use only the installed `embassy` CLI. Treat it as the sole facade over the private, local Embassy control socket. Keep this skill repo-scoped; do not install, copy, or modify provider configuration.
9
9
 
10
- Provider-authorized operations require exactly one principal accepted by that command: inherited Codex identity, inherited Claude identity, or a shell-peer alias plus token. Stop on a missing or conflicting principal; never choose one on the caller's behalf. Operator-only `serve`, health, status, refresh, select, and unselect commands do not infer a provider principal. A shell-peer token authorizes only that peer's send, reply, await, receipt, and unregister operations; it never authorizes `pair` or `unpair`. Those commands carry the inherited `CODEX_THREAD_ID` as attestation when run inside a Codex task and otherwise fail closed; only the operator-facing live dashboard creates or removes an edge without that task attestation.
10
+ Registration, send, reply, await, receipt, and unregister operations require the exact principal accepted by that command: inherited Codex identity, inherited Claude identity, or a shell-peer alias plus token. Stop on a missing or conflicting required principal; never choose one on the caller's behalf. `pair`, `unpair`, `select-claude`, and `unselect-claude` are same-UID control-plane operations authorized by the private control socket, not by inherited provider identity. Agents remain norm-bound to create or remove only the exact edges the user chose; paired-mode membership is rechecked at delivery.
11
11
 
12
- If `CALLER_IDENTITY_CONFLICT` reports that both agent identities were inherited, explain that the Codex App Server daemon may have been started inside an agent session. Tell the operator to run `codex app-server daemon restart` from a normal terminal. Never inspect, print, clear, or copy either inherited value. Without the dual-identity hint, report only the generic fail-closed result; the caller may simply be the wrong principal.
12
+ If `CALLER_IDENTITY_CONFLICT` reports both inherited identities, strip only the unwanted identity at the call site: use `env -u CLAUDE_CODE_MESSAGING_SOCKET embassy …` for a Codex-side call, or `env -u CODEX_THREAD_ID embassy …` for a Claude-side call. Never inspect, print, clear, or copy either inherited value. Without the dual-identity hint, report only the generic fail-closed result; the caller may simply be the wrong principal.
13
13
 
14
14
  ## Select a peer
15
15
 
16
- Address a Claude session by its latest `name@host` or by a user-supplied native session UUID. The UUID is the stable identity; the name is only the current live index. The gateway stores no historical names, so an old name stops resolving immediately after a rename. A broker without a federation inventory defaults to `this-mac`; configured allowlisted Embassy nodes exchange bounded public route catalogs and destination-owned handoffs over fixed attach-only SSH. Ask the user to choose a selector when it is ambiguous.
16
+ Address a Claude session by its latest `name@host` or by a user-supplied native session UUID. The UUID is the stable identity; the name is only the current live index. The gateway stores no historical names, so an old name stops resolving immediately after a rename. The mandatory private `nodes.json` names the local host; configured allowlisted Embassy nodes exchange bounded public route catalogs and destination-owned handoffs over fixed attach-only SSH. Ask the user to choose a selector when it is ambiguous.
17
17
 
18
18
  Run `embassy status` to read the current snapshot. Run `embassy refresh-dashboard` when passive live discovery is authorized. Claude Code's native `ListAgents` includes genuine Claude sessions plus each explicitly advertised `codex-*` Embassy peer.
19
19
 
20
- Read the status snapshot's `availablePeers` as sanitized current-name candidates. Native records carrying Embassy's supported explicit versioned advertisement marker are excluded because they are not Claude destinations; a genuine unmarked Claude session remains visible even when its name starts with `codex-*`. A send never pairs with a Claude session automatically. Create the exact user-chosen edge with `pair` — or the one-task shorthand `select-claude` — before sending; an unpaired destination is not routable.
20
+ Read the status snapshot's `availablePeers` as sanitized current-name candidates. Native records carrying Embassy's supported explicit versioned advertisement marker are excluded because they are not Claude destinations; a genuine unmarked Claude session remains visible even when its name starts with `codex-*`. A send never pairs with a Claude session automatically. Select the Claude route, then create the exact user-chosen edge with `pair` before sending; an unpaired destination is not routable.
21
21
 
22
22
  Accept a Claude session UUID only when the user supplies it or it is already part of the current task context. Never discover one by scanning history or configuration, and never infer a peer from a thread ID, process ID, working directory, socket path, or title.
23
23
 
24
24
  ## Check the gateway
25
25
 
26
+ Before any Embassy client call that talks to the broker, the CLI reads the state
27
+ directory and `nodes.json`, then connects to the private control socket. Grant a
28
+ sandboxed Codex task that directory as a writable root, or approve equivalent
29
+ local access. Do not relocate state or start a second broker to work around a denial.
30
+
26
31
  Run this before a state-changing operation:
27
32
 
28
33
  ```sh
29
34
  embassy health
30
35
  ```
31
36
 
32
- If Embassy is unavailable, stop and report that it must be started in a trusted local terminal with `embassy serve`. `GATEWAY_INSTANCE_IN_USE` means an Embassy or recognized legacy lock already owns this login account; stop that foreground process rather than changing `EMBASSY_STATE_DIR`. If no legacy process remains, the operator may remove only the exact stale legacy controller lock and retry. Do not launch a background copy, retry in a loop, discover sockets, or fall back to a provider CLI.
37
+ If Embassy is unavailable, follow any accompanying denied-access or unsafe-path guidance first. Only when no access or unsafe-path condition is reported, stop and report that it must be started in a trusted local terminal with `embassy serve`. `GATEWAY_INSTANCE_IN_USE` means an Embassy or recognized legacy lock already owns this login account; stop that foreground process rather than changing `EMBASSY_STATE_DIR`. If no legacy process remains, the operator may remove only the exact stale legacy controller lock and retry. Do not launch a background copy, retry in a loop, discover sockets, or fall back to a provider CLI.
33
38
 
34
39
  Embassy presents Claude, Codex, DeepSeek, Grok, and shell peers as first-class providers. Runtime status is best-effort: use observation freshness, connector health, observed metadata, and the last safe code to explain what is available now. Provider versions are diagnostic metadata, not routing authority; the release-owned offline support matrix is the record of tested artifacts, capabilities, limitations, and test dates. There is no agent or operator compatibility action. Report a degraded surface and stop rather than sending a test message or trying to override a failed operation.
35
40
 
@@ -83,13 +88,13 @@ Create one explicit cross-provider edge by naming both ends. Each endpoint must
83
88
  embassy pair --from codex-reviewer@this-mac --to advisor@this-mac
84
89
  ```
85
90
 
86
- Pairs are additive and bounded; many edges may coexist, and `pair` never retires another edge. Run `pair` and `unpair` from inside a registered Codex task so the CLI reads the inherited `CODEX_THREAD_ID`; a plain operator shell fails closed with `CODEX_IDENTITY_REQUIRED` use the live dashboard or the one-task shorthand instead. Remove exactly the named edge:
91
+ Pairs are additive and bounded; many edges may coexist, and `pair` never retires another edge. The same-UID private control socket authorizes this control-plane mutation; Embassy does not attest an inherited provider identity for pair or unpair. Create or remove only the exact user-chosen edge. Remove it by naming both endpoints:
87
92
 
88
93
  ```sh
89
94
  embassy unpair --from codex-reviewer@this-mac --to advisor@this-mac
90
95
  ```
91
96
 
92
- When the Codex end is unambiguous inherited from the calling task, or the sole registered task — the one-task shorthand forms or removes the same edge:
97
+ Claude selection is a separate operator control and creates no permission edge:
93
98
 
94
99
  ```sh
95
100
  embassy select-claude --alias advisor@this-mac
@@ -101,17 +106,17 @@ Or address the same logical session directly by UUID:
101
106
  embassy select-claude --session 123e4567-e89b-42d3-a456-426614174000
102
107
  ```
103
108
 
104
- Remove the same one-task edge by naming the Claude endpoint:
109
+ Remove the selected Claude route by naming that endpoint:
105
110
 
106
111
  ```sh
107
112
  embassy unselect-claude --alias advisor@this-mac
108
113
  ```
109
114
 
110
- With zero or several possible Codex ends, the shorthands fail closed and name the explicit verb; never guess an end on the caller's behalf.
115
+ After selection, use explicit `pair --from <alias> --to <alias>` before sending; never infer or guess an edge on the user's behalf.
111
116
 
112
117
  Let the gateway resolve either selector against the current genuine Claude discovery snapshot. It refreshes process and socket coordinates by UUID; those transport details are never caller inputs. If discovery is ambiguous, incompatible, or unavailable, stop on the result.
113
118
 
114
- If the paired session is offline or was renamed while Embassy was stopped, the user may instead supply its UUID with `--session`. Pairing and removal manage only the gateway edge. They do not start, interrupt, configure, or terminate Claude Code.
119
+ If the selected session is offline or was renamed while Embassy was stopped, the user may recover selection by supplying its UUID with `select-claude --session`. UUID recovery applies only to selection; `pair` still requires two aliases. `unselect-claude` removes the selected route, removes its incident consent edges, and settles their in-flight work from the durable attempt phase. These controls do not start, interrupt, configure, or terminate Claude Code.
115
120
 
116
121
  ## Register a Codex task
117
122
 
@@ -160,7 +165,7 @@ Pass a non-empty UTF-8 body through standard input. Never place message text in
160
165
  From a registered Codex task to a paired Claude session:
161
166
 
162
167
  ```sh
163
- embassy send-to-claude \
168
+ embassy send \
164
169
  --from codex-reviewer@this-mac \
165
170
  --to advisor@this-mac <<'GATEWAY_MESSAGE'
166
171
  Please review the current approach and note the main risk in your own session.
@@ -176,7 +181,7 @@ The foreground launcher supports native bidirectional messaging for each explici
176
181
 
177
182
  Direction determines timing. Once routing and pre-write checks pass, every Claude-bound send or correlated reply writes immediately to Claude's native mailbox regardless of its observed busy or idle state. Do not wait for Claude to become idle or report its busy state as a queue reason. `transport_written` is the terminal `delivered` boundary for that direction and means mailbox write, not read or consumption. Codex-bound ordinary work remains idle/turn-boundary gated; only the exact `STEER:` behavior below may target the active turn's next tool-call boundary.
178
183
 
179
- An accepted send returns a public conversation token and a fresh delivery token. The conversation token and reply capability are memory-only. The delivery token is an opaque correlation handle, exactly `dlv_` plus 24 base64url characters, retained only with its bounded private v3 message row. Use the exact returned values only for their intended CLI calls; do not construct, shorten, log, persist yourself, or place either token in an agent-created file.
184
+ An accepted send returns a public conversation token and a fresh delivery token. The conversation token and reply capability are memory-only. The delivery token is an opaque correlation handle, exactly `dlv_` plus 24 base64url characters, retained only with its bounded private v4 message row. Use the exact returned values only for their intended CLI calls; do not construct, shorten, log, persist yourself, or place either token in an agent-created file.
180
185
 
181
186
  Use exactly one send for one user-authorized message. A send never selects a Claude session automatically. Do not automatically retry, fan out, hand-roll a poll loop, or fall back to Claude Code's native `SendMessage`.
182
187
 
@@ -222,7 +227,7 @@ embassy wait-delivery --token dlv_0123456789abcdefghijklmn
222
227
 
223
228
  It checks every 250 ms and emits only a terminal result. It stops at the delivery deadline plus 3 seconds; an unknown token fails immediately. Exit `0` means `delivered`; every other terminal state (`unconfirmed`, `expired`, `failed`, `ambiguous`, or `cancelled`) preserves its exact JSON result and uses the shared delivery-failure exit `6`. An unknown token exits `3`. A local waiter timeout exits `4`, is not a terminal result, and is not permission to resend. A terminal result closes only that delivery attempt: `delivered` does not promise a reply, and `unconfirmed` or `ambiguous` must never be retried automatically.
224
229
 
225
- The private v3 message ledger is bounded. Under pressure, its oldest terminal row may be evicted while active `queued` or `stalled` rows are retained. A token absent from bounded retention returns `{"found":false}`.
230
+ The private v4 message ledger is bounded. Under pressure, its oldest terminal row may be evicted while active `queued` or `stalled` rows are retained. A token absent from bounded retention returns `{"found":false}`.
226
231
 
227
232
  ## Interpret queue state
228
233
 
@@ -230,7 +235,7 @@ Treat `accepted` as gateway ownership, not proof that the peer read or answered
230
235
 
231
236
  For native Claude-to-Codex ingress, Embassy first attempts immediate dispatch. A terminal result observed before the one-second prompt boundary produces only its terminal acknowledgement; native `held` is sent only when the body truly remains queued or dispatch is still nonterminal at that boundary, followed later by the terminal acknowledgement. Claude's rendered “approved and released” notice means only that the paired-consent gateway accepted and released the body to the recipient queue — released is not read, and no human approval is implied. The default `merged` notice policy separately sends at most one nonterminal stall user frame exactly at `floor(messageDeadlineMs / 2)`, containing only a bounded pending age and allowlisted reason. The operator may choose `verbose` to retain the additional terminal diagnostic user frame or `quiet` to suppress gateway-authored user-frame notices; native status and dashboard truth do not change. Codex-bound ordinary work queues while the Codex task is active or temporarily unavailable. Only when the user explicitly asks to steer the active Codex turn may a Claude sender put the exact prefix `STEER:` at the beginning of the body. Embassy uses the exact accepted operation's same-session capability at the next tool-call boundary, never mid-generation or by interruption. Clean boundary refusal returns it to the normal queue; the cap is three steers per exact active operation. Embassy never calls `turn/interrupt` and never retries an ambiguous write.
232
237
 
233
- Do not synthesize `STEER:`, use it from Codex to Claude, approve permissions, widen tools, alter inbound-message policy, or interrupt a turn to force delivery. Report `held`, refused, incompatible, full, expired, unavailable, or `STEER_QUEUE_SUPERSEDED` outcomes or safe error codes without treating them as additional `delivery-status` states and without retrying. Native receipt settlement follows the originating Claude session's stable UUID and revalidates its current endpoint before every stall or terminal write; names, PIDs, and sockets are not receipt identity. Ordinary process/socket rotation for the same Claude UUID is refreshed for that write. After a gateway restart, queued or reserved messages and their delivery tokens/status remain inspectable in the bounded private v3 ledger and may resume once within their deadline and attempt budget against the same exact route and consent edge. Armed work settles `ambiguous`; accepted work settles `unconfirmed`; neither is replayed. Conversations, reply/native capabilities, raw provider frames, callbacks, pending replies, and socket paths remain memory-only. Best-effort observation may refresh what status displays, but it never authorizes or gates delivery.
238
+ Do not synthesize `STEER:`, use it from Codex to Claude, approve permissions, widen tools, alter inbound-message policy, or interrupt a turn to force delivery. Report `held`, refused, incompatible, full, expired, unavailable, or `STEER_QUEUE_SUPERSEDED` outcomes or safe error codes without treating them as additional `delivery-status` states and without retrying. Native receipt settlement follows the originating Claude session's stable UUID and revalidates its current endpoint before every stall or terminal write; names, PIDs, and sockets are not receipt identity. Ordinary process/socket rotation for the same Claude UUID is refreshed for that write. After a gateway restart, queued or reserved messages and their delivery tokens/status remain inspectable in the bounded private v4 ledger and may resume once within their deadline and attempt budget against the same exact route and consent edge. Armed work settles `ambiguous`; accepted work settles `unconfirmed`; neither is replayed. Conversations, reply/native capabilities, raw provider frames, callbacks, pending replies, and socket paths remain memory-only. Best-effort observation may refresh what status displays, but it never authorizes or gates delivery.
234
239
 
235
240
  ## Preserve the boundary
236
241