herdr-link 0.2.0
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 +28 -0
- package/LICENSE +21 -0
- package/PROTOCOL.md +244 -0
- package/README.md +159 -0
- package/README.zh-CN.md +159 -0
- package/dist/herdr-link.mcp.js +796 -0
- package/dist/herdr-link.opencode.js +535 -0
- package/docs/mcp-wiring.md +275 -0
- package/package.json +65 -0
- package/scripts/mcp-probe.mjs +41 -0
- package/src/herdr.ts +535 -0
- package/src/mcp.ts +582 -0
- package/src/opencode.ts +180 -0
- package/src/pi.ts +170 -0
- package/src/protocol.ts +284 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
|
+
|
|
6
|
+
## [0.2.0] - 2026-08-25
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Self identity bootstrap** (PROTOCOL.md §6.3): when the current pane occupant is recognized by Herdr but has no valid Agent Name, adapters run `ensureSelfName()` — once at adapter startup and as a fallback inside every communication path — to assign a generated `hl-<hex>` name via `agent rename`, confirmed by re-reading the authoritative live record. Existing valid names are never rewritten; collisions (`agent_name_taken`) regenerate within a bounded 3-attempt budget; the initial probe carries an equally small detection-readiness retry for freshly launched occupants; concurrent bootstraps coalesce into one rename sequence; nothing is persisted (Herdr remains the lifecycle authority).
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- PROTOCOL.md §6.2/§7/§8/§9: identity readiness now permits the scoped internal bootstrap; `SELF_UNNAMED` narrowed to "Link attempted but failed to establish a stable Agent Name"; `agent rename` moved from the blanket command forbidden list to a §6.3-only scoped exception (never model-facing); Non-goals exclude general Agent Name management while allowing the one-shot self bootstrap.
|
|
15
|
+
|
|
16
|
+
## [0.1.0] - 2026-08-25
|
|
17
|
+
|
|
18
|
+
First public release of Herdr Link: an on-demand cross-agent interoperability layer running inside Herdr sessions.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **`herdr-link/1` protocol** (`PROTOCOL.md`, canonical spec): self-describing envelope format with reply correlation, two-tier capability surface (dormant `herdr_link` gateway + Tier 1 tools), compact Communication Contract injection, and a local error model (`NOT_IN_HERDR` / `SELF_UNNAMED` / `PEER_NOT_FOUND` / `SEND_FAILED` / `CLOSE_FAILED`).
|
|
23
|
+
- **Lazy capability activation**: runtimes present only a minimal gateway until an explicit Herdr intent or an inbound self-describing Link message activates peers/send/close for the current runtime session; activation state is in-memory only.
|
|
24
|
+
- **Pi adapter** (`src/pi.ts`): native extension using deferred Tier-1 tools via `setActiveTools`; contract injected on activation only.
|
|
25
|
+
- **OpenCode adapter** (`src/opencode.ts`): single-file plugin bundle presenting one `herdr_link` dispatcher tool (`peers`/`send`/`close` actions); per-session contract injection via system-prompt transform.
|
|
26
|
+
- **Shared stdio MCP server** (`src/mcp.ts`, zero-dependency JSON-RPC) for Claude Code, Codex, and AGY: empty `tools/list` outside Herdr, dormant/active gating with `notifications/tools/list_changed`, gateway action fallback for non-refreshing hosts.
|
|
27
|
+
- **Same-workspace guard**: live identity/workspace resolution via `agent get` on every call; cross-workspace discovery/send/close are out of scope by design.
|
|
28
|
+
- Wiring guides for all MCP hosts in `docs/mcp-wiring.md`; stdio probe script `scripts/mcp-probe.mjs`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LZHcode1986
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/PROTOCOL.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Herdr Link Protocol — `herdr-link/1`
|
|
2
|
+
|
|
3
|
+
> 本文件是 Herdr Link 协议的唯一规范文本(canonical spec)。所有 Runtime Adapter 必须实现本文件定义的语义;任何修改必须先修改本文件。
|
|
4
|
+
> 设计决策记录保存在本机开发环境,不随协议发布。
|
|
5
|
+
|
|
6
|
+
## 1. Protocol Identifier
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
herdr-link/1
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
版本号放在 Envelope 的 `protocol` 字段中,不另建 negotiation service。
|
|
13
|
+
|
|
14
|
+
## 2. Message Envelope
|
|
15
|
+
|
|
16
|
+
### 2.1 发送
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"protocol": "herdr-link/1",
|
|
21
|
+
"id": "hl_mep7abc_4f8k2n",
|
|
22
|
+
"from": "brain",
|
|
23
|
+
"to": "reviewer",
|
|
24
|
+
"message": "请检查这个设计。"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2.2 回复
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"protocol": "herdr-link/1",
|
|
33
|
+
"id": "hl_mep7def_9q3r5s",
|
|
34
|
+
"from": "reviewer",
|
|
35
|
+
"to": "brain",
|
|
36
|
+
"reply_to": "hl_mep7abc_4f8k2n",
|
|
37
|
+
"message": "检查完成。"
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
发送与回复共用同一个 Envelope 和同一个工具;回复仅增加 `reply_to`,不存在独立的 reply 工具或消息类型。
|
|
42
|
+
|
|
43
|
+
### 2.3 字段定义
|
|
44
|
+
|
|
45
|
+
| 字段 | 必填 | 生成方 | 语义 |
|
|
46
|
+
|---|---:|---|---|
|
|
47
|
+
| `protocol` | 是 | Adapter | 固定为 `herdr-link/1`;模型不可提交 |
|
|
48
|
+
| `id` | 是 | Adapter | 消息唯一 ID,格式为 `hl_<timestamp>_<random>`;`timestamp` 与 `random` 均为非空小写字母数字串 |
|
|
49
|
+
| `from` | 是 | Adapter | 当前 Agent Name(从 Herdr identity 即时解析);模型不可自行声明 |
|
|
50
|
+
| `to` | 是 | Model input → Adapter validation | 目标 Agent Name;必须匹配 `[a-z][a-z0-9_-]{0,31}` 且为当前 workspace 内的 live named peer |
|
|
51
|
+
| `reply_to` | 否 | Model input → Adapter validation | 被回复消息的合法 `herdr-link/1` `id` |
|
|
52
|
+
| `message` | 是 | Model | 非空业务 payload(至少包含一个非空白字符,可含 JSON/YAML);Link 不解析其语义 |
|
|
53
|
+
|
|
54
|
+
### 2.4 不进入 Envelope 的字段
|
|
55
|
+
|
|
56
|
+
V1 不定义:`task_id`、`status`、`result`、`error`、`runtime`、`model`、`priority`、`timeout`、`workflow`、`stage`、`permission`、`evidence`、`receipt`、`workspace_id`、`pane_id`。
|
|
57
|
+
|
|
58
|
+
上层业务需要结构化 payload 时,将 JSON/YAML/文本放入 `message`;Herdr Link 不解释其中业务语义。**workspace scope 是本地 Adapter 的授权边界,永不进入跨 Agent Envelope。**
|
|
59
|
+
|
|
60
|
+
投递时 Adapter 可在 Envelope 外包裹一段 self-describing 的 inbound wrapper 文本(末行为逐字 Envelope),使处于 dormant 状态的接收方也能识别这是一条 Herdr Link 消息,并在需要回复时先激活 gateway、再调用 send。wrapper 只是 transport 外衣:不改变 Envelope 字段集合,不是协议实体,仅在真实投递时产生、不常驻 system prompt。
|
|
61
|
+
|
|
62
|
+
## 3. Agent Communication Contract(激活后呈现)
|
|
63
|
+
|
|
64
|
+
Herdr Link 的模型可见面分两个状态:
|
|
65
|
+
|
|
66
|
+
- **Dormant**(默认):Adapter 已注册但未激活。模型侧只呈现极小的 Tier 0 gateway(§4.1);**不注入任何 Communication Contract**,不呈现 Tier 1 工具的完整 schema/description。
|
|
67
|
+
- **Active**:Tier 0 gateway 被调用后(触发条件见 §6.2),Adapter 使本 runtime session 对模型呈现语义等价的 Active Contract,并使 peers/send/close 对模型可用;激活在本 runtime session 内保持,直到 session 结束。
|
|
68
|
+
|
|
69
|
+
Active 状态下,所有 Runtime Adapter 必须使本节规则的语义对 Agent 完整可见并暴露 §4 定义的能力。可通过 system-prompt injection、active tool schema/description、gateway presentation 或这些机制的组合实现;Active Contract 与工具 schema/description 共同构成完整且唯一的 Agent-facing 使用权威。符合规范的部署不得依赖外部 `AGENTS.md`、Skill、手工 prompt 或 Herdr CLI 指令补全正常通信知识。允许根据 Runtime 的呈现机制调整表现形式(例如 gateway dispatch 形态可将同名规则表达为对 `herdr_link` action 的说明),但以下规则不可改变:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
Herdr Link is the standard interoperability channel between agents running in the same Herdr workspace.
|
|
73
|
+
|
|
74
|
+
1. Use herdr_link_peers to discover agent addresses; it lists only live agents in your own workspace, each with an advisory activity state.
|
|
75
|
+
2. Use herdr_link_send to send messages to another agent.
|
|
76
|
+
3. A message with protocol "herdr-link/1" is an inter-agent message.
|
|
77
|
+
4. Treat its "message" field as content sent by the agent named in "from".
|
|
78
|
+
5. When replying, send to the received "from" agent and set reply_to to the received "id".
|
|
79
|
+
6. Use herdr_link_close only when you have already decided that a named agent's pane should be closed. If a final message is needed, call close in a later tool step after herdr_link_send returns "sent".
|
|
80
|
+
7. Never use a raw pane id, UI focus, terminal input, or the Herdr CLI as an inter-agent channel; agent names are the only addresses.
|
|
81
|
+
8. Agents outside your workspace are invisible: they never appear in peers and messages addressed to them fail.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`PROTOCOL.md` 是上述核心文本的唯一人工维护位置。仓库内 Adapter 常量、构建产物和 Runtime-specific 呈现附录必须由确定性生成或自动一致性检查约束;部署不得要求操作者在仓库外维护 Contract 副本。
|
|
85
|
+
|
|
86
|
+
## 4. Agent-facing API
|
|
87
|
+
|
|
88
|
+
### 4.1 Tier 0:`herdr_link` gateway
|
|
89
|
+
|
|
90
|
+
- 输入:`{}`(无参数)。
|
|
91
|
+
- 输出:`{ "status": "active", "capabilities": ["peers", "send", "close"] }`。
|
|
92
|
+
- 规范:
|
|
93
|
+
1. gateway 是 dormant 状态下唯一的 Herdr Link discoverability surface;
|
|
94
|
+
2. 幂等:重复调用仍返回 active;
|
|
95
|
+
3. 激活后在本 runtime session 内保持 active(§6.2);activation 只存在于内存,不持久化、不跨 session 恢复;
|
|
96
|
+
4. gateway 本身不做 peer discovery、不发消息、不关 pane;
|
|
97
|
+
5. 调用 gateway 不要求当前 Agent 已拥有 Agent Name。
|
|
98
|
+
|
|
99
|
+
### 4.2 Tier 1:`herdr_link_peers`
|
|
100
|
+
|
|
101
|
+
- 输入:无。
|
|
102
|
+
- 输出:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"self": { "name": "brain", "state": "working" },
|
|
107
|
+
"peers": [
|
|
108
|
+
{ "name": "reviewer", "state": "idle" }
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- 语义:
|
|
114
|
+
- `self.name` 由 Adapter 从 Herdr live identity 即时解析,绝不硬编码;
|
|
115
|
+
- `state` 直接映射 Herdr authoritative `AgentStatus`:`idle | working | blocked | done | unknown`;无法识别的取值归一化为 `unknown`;
|
|
116
|
+
- `peers` 只包含当前 authoritative workspace 内有稳定 Agent Name 的 live Agent,排除 self;
|
|
117
|
+
- 不返回 workspace_id / pane_id / tab_id / terminal ID;
|
|
118
|
+
- `state` 仅供观察:不排序优先级、不解释业务含义、**不作为 send / close 的前置条件**。
|
|
119
|
+
|
|
120
|
+
### 4.3 Tier 1:`herdr_link_send`(含回复)
|
|
121
|
+
|
|
122
|
+
- 输入:`{ "to": string, "message": string, "reply_to": string (可选) }`;`to`、`message` 与 `reply_to` 必须满足 §2.3;
|
|
123
|
+
- 输出:`{ "status": "sent", "id": string, "to": string }`
|
|
124
|
+
- 语义:
|
|
125
|
+
- 每次 send 都实时重新解析 self 与 target 的 live 记录并执行 same-workspace guard(§5),不缓存;
|
|
126
|
+
- 不要求先调用 `peers`;不依赖目标 `state`;
|
|
127
|
+
- `status=sent` 只表示 Herdr 接受消息投递;不表示对方完成任务;
|
|
128
|
+
- 不等待 reply;不自动 poll;不维护 pending request 状态;不执行 retry policy;
|
|
129
|
+
- 投递给 Herdr `agent prompt` 的内容是 §2 所述的 self-describing inbound wrapper,Envelope 本身逐字不变。
|
|
130
|
+
|
|
131
|
+
### 4.4 Tier 1:`herdr_link_close`
|
|
132
|
+
|
|
133
|
+
- 输入:`{ "agent": string }` —— 只接受 Agent Name,不接受 raw pane ID。
|
|
134
|
+
- 正常输出:`{ "status": "closed", "agent": string }`
|
|
135
|
+
- 规范:
|
|
136
|
+
1. 如果调用方需要发送最终消息,必须先等待 `herdr_link_send` 返回 `status=sent`,再在后续工具步骤调用 `herdr_link_close`;
|
|
137
|
+
2. 每次 close 都实时重新解析 self 与 target 的 live 记录并通过 same-workspace guard(§5),然后取 target 当前的 authoritative `pane_id`,再调用 `pane close <pane_id>`;不缓存 pane ID;
|
|
138
|
+
3. 不依赖目标 `state`;不允许默认关闭 focused pane;不允许 `--current` / UI focus fallback;
|
|
139
|
+
4. 不要求先 `release-agent`;V1 的资源关闭原语就是 Herdr `pane.close`;
|
|
140
|
+
5. 如果关闭的是调用 Agent 自己的 pane,进程可能在工具响应完整返回前终止;调用方不得依赖 self-close 的返回值完成后续业务动作;
|
|
141
|
+
6. Herdr 返回失败时直接失败,不猜测替代目标。
|
|
142
|
+
|
|
143
|
+
### 4.5 工具命名呈现
|
|
144
|
+
|
|
145
|
+
- Canonical 名固定为 Tier 0 `herdr_link` 与 Tier 1 `herdr_link_peers` / `herdr_link_send` / `herdr_link_close`;
|
|
146
|
+
- Runtime 可因宿主机制以不同形态呈现:
|
|
147
|
+
- **true deferred tools**:四个工具均为独立注册工具,dormant 时仅 gateway 在模型可见集合中,激活后 Tier 1 进入可见集合(如 Pi 的动态工具 API);
|
|
148
|
+
- **prefix 型独立工具**(如 MCP 宿主的 `mcp__<namespace>__<tool>`):呈现名的结尾必须是完整 canonical 名;
|
|
149
|
+
- **wrapper 形式**(如 AGY 的 `ServerName`/`ToolName` 参数化调用):`ToolName` 即 canonical 名;
|
|
150
|
+
- **single-gateway dispatch**:模型面只有 `herdr_link` 一个工具,Tier 1 能力以 `action` 参数分发;此时 active presentation 必须把 §3 规则完整映射到 gateway action 上;
|
|
151
|
+
- 无论哪种形态,呈现层与 canonical 名之间必须有确定性映射;入参/出参 schema、错误语义与调用时序约束完全一致;
|
|
152
|
+
- Active presentation 必须同时声明该 Runtime 的实际呈现方式与 dormant/active 行为,使模型无需猜测即可正确激活和调用。
|
|
153
|
+
|
|
154
|
+
逻辑能力集合在所有形态下恒为:**activate / peers / send(+reply) / close**。
|
|
155
|
+
|
|
156
|
+
## 5. Peer 地址模型与通信域
|
|
157
|
+
|
|
158
|
+
- 唯一公开地址是 Herdr Agent Name(`[a-z][a-z0-9_-]{0,31}`)。
|
|
159
|
+
- **唯一性由 Herdr 强制**(实测:向已占用名字 rename 返回 `agent_name_taken`)。同一 Herdr daemon 内 Agent Name 跨 workspace 全局共享、全局解析,同一时刻不存在两个同名 live agent——按名字解析不会歧义。
|
|
160
|
+
- **Herdr Link 的通信域是当前 authoritative workspace。** 公开 peer 定义收窄为:*当前 Agent live 记录中 workspace 相同的 named live Agent*。因此 `Herdr 可寻址 ≠ Herdr Link peer`:Herdr 底层的跨 workspace 寻址能力不通过 Link 暴露。
|
|
161
|
+
- self 与 target 的当前 workspace 一律来自每次调用即时执行的 `agent get`(以 `HERDR_PANE_ID` 解析 self),fresh 读取、不缓存。环境变量 `HERDR_WORKSPACE_ID` / `HERDR_TAB_ID` 只是存在性信号:pane 被 move 跨 workspace 后进程仍保留 launch-time 旧值,**不得作为当前 workspace 的权威**。
|
|
162
|
+
- 跨 workspace 目标(以及非法名、不存在的名、workspace 未上报的目标)对模型统一表现为 `PEER_NOT_FOUND`;不得返回区分性的「存在于其他 workspace」信息,不泄漏其他 workspace topology。
|
|
163
|
+
- Agent Name 跟随 pane occupant;agent 退出/释放/替换时清除。peer 列表是瞬时的,每次调用即时生成,不缓存。
|
|
164
|
+
- 命名空间全局共享意味着多项目可能撞名:部署者应按项目前缀命名 Agent(如 `proofloop-brain`)。即便如此,Link 也只会在同一 workspace 内发现它们。
|
|
165
|
+
- `peers` 可寻址不等于支持 Link Contract:V1 不验证目标 Runtime 是否安装了 Herdr Link Adapter;部署者负责保证参与互通的各 Runtime 安装了对应 Adapter。
|
|
166
|
+
|
|
167
|
+
## 6. Adapter Contract
|
|
168
|
+
|
|
169
|
+
一个 Runtime 被视为支持 Herdr Link,必须由同一 Runtime Adapter 交付闭环满足四项逻辑能力:
|
|
170
|
+
|
|
171
|
+
1. **Activate**:提供 `herdr_link` gateway 等价能力(§4.1);
|
|
172
|
+
2. **Expose Active Contract Semantics**:active 后让模型完整知道本协议第 3 节的规则;允许通过 system-prompt injection、active tool schema/description、gateway presentation 或组合实现;dormant 时必须**不**暴露 Tier 1 Contract 语义;
|
|
173
|
+
3. **Expose Peers / Send**:提供 `herdr_link_peers`、`herdr_link_send`(含 reply_to 回复)等价能力;
|
|
174
|
+
4. **Expose Close**:提供 `herdr_link_close` 等价能力。
|
|
175
|
+
|
|
176
|
+
“同一 Runtime Adapter”指一个可独立安装和验证的 Runtime-specific 交付单元;它可以由多个宿主接线点组成(例如 MCP tools + Runtime hook),但不得把外部 Agent 指令文件或操作者维护的 Contract 副本当作第五项依赖。
|
|
177
|
+
|
|
178
|
+
Adapter 可通过 Extension、Hook、Plugin、MCP Tool 或 Runtime 原生 tool system 实现;不强制实现语言。V1 不创建统一 Adapter Framework(无 BaseAdapter / registry / plugin loader / daemon)。工具命名呈现须符合 §4.5。已知的合规呈现形态:
|
|
179
|
+
|
|
180
|
+
- **true deferred tools**(如 Pi):四工具全部注册,`session_start` 时把 Tier 1 移出 active 集合,gateway 以加性方式启用 Tier 1;close 保持顺序执行以保证 send 先完成;
|
|
181
|
+
- **single-gateway dispatch**(宿主无公开的动态启停 API 时,如 OpenCode):模型面常驻且仅有一个极小 `herdr_link` dispatcher,空参调用幂等激活本 session,随后以 `action: peers|send|close` 分发到同一控制层;Active Contract semantics 仅在已激活 session 暴露;
|
|
182
|
+
- **shared MCP:listChanged 优先 + gateway fallback**(Claude Code / Codex / AGY 等):dormant `tools/list` 只返回 gateway;声明 `tools.listChanged` capability,激活时发射一次 `notifications/tools/list_changed`;active `tools/list` 返回 gateway + Tier 1;不响应刷新的 Host 通过 gateway 显式 action 分发保持全功能。MCP activation 按 stdio 连接(即宿主为本 session 拉起的 server 进程)记忆,连接结束即回到 dormant。
|
|
183
|
+
|
|
184
|
+
### 6.1 环境门控(Environment Gate)
|
|
185
|
+
|
|
186
|
+
- 只有 `HERDR_ENV=1`、`HERDR_BIN_PATH` 与 `HERDR_PANE_ID` 均存在时 Adapter 才注册任何工具;否则保持完全 no-op——不注册工具、不注入 Contract(MCP 形态下 `tools/list` 返回空集)。
|
|
187
|
+
- `HERDR_BIN_PATH` 失效(binary 被更新/删除导致 spawn 失败)、CLI transport 失败或响应非法 JSON 属于 Herdr 环境不可用,归类为 `NOT_IN_HERDR`(§7),不得改判为操作级错误码。
|
|
188
|
+
|
|
189
|
+
### 6.2 Activation 与 Communication Readiness
|
|
190
|
+
|
|
191
|
+
- **Dormant 为默认态**。满足环境门控后,模型侧只看到 gateway;不注入 Contract、不呈现 Tier 1 完整 schema、不加载官方 Herdr Skill、不产生后台轮询/监听。
|
|
192
|
+
- **激活触发仅有两个**:用户显式要求使用 Herdr(explicit Herdr intent),或收到一条 self-describing 的 inbound `herdr-link/1` 投递(§2)。两者都表现为模型调用 gateway;inbound 触发不要求宿主具备 prompt 拦截能力——wrapper 文本本身引导模型调用 gateway。
|
|
193
|
+
- **Once-per-runtime-session**:激活后在当前 runtime session 内保持 active,不因后续用户消息不再提及 Herdr 而回退;新 runtime session 重新从 dormant 开始。activation 是内存中的 session 局部状态,不持久化、不写文件、不进 DB。
|
|
194
|
+
- **Communication readiness**:已激活的当前 pane occupant 还必须拥有合法、稳定的 live Agent Name,才能作为 Envelope 的 `from` 使用 `herdr_link_peers` / `herdr_link_send` / reply。
|
|
195
|
+
- 已激活但当前 occupant 未命名时,Herdr Link 先执行一次内部 self identity bootstrap(§6.3):为已被 Herdr 识别但尚未命名的当前 occupant 自动生成并绑定一个 Link 前缀的临时 Agent Name 后再正常通信;occupant 尚未被 Herdr 检测或 bootstrap 最终失败时,通信调用返回 `SELF_UNNAMED`。除 §6.3 的碰撞重生外不自动 retry;不猜测名字、不缓存旧名字。
|
|
196
|
+
- 当前 occupant 已有合法 Agent Name 时绝不改名:用户指定名保持不变,仅在无合法名时生成新名。Link 不持久化 Agent Names;名字的生命周期与恢复仍由 Herdr 和部署/编排层负责,Link 只在每次通信调用时消费 live identity。显式 `herdr_link_close(agent)` 仍按目标 Agent Name 解析,不把调用方是否已命名作为额外条件。
|
|
197
|
+
|
|
198
|
+
### 6.3 Self Identity Bootstrap(Adapter 内部机制)
|
|
199
|
+
|
|
200
|
+
- 触发时机:Adapter 进入有效 Herdr 环境后执行一次 `ensureSelfName()`;每条通信路径都通过 `getSelfContext()` 重新解析 self identity 并完成 bootstrap 兜底。当 self 解析暂时报告 occupant 尚未被 Herdr 检测时,初始探测做小预算有界 best-effort 就绪重试;就绪时序是 Adapter 实现细节,不依赖、不耦合 Herdr 内部 detection cadence,也不构成对检测窗口的正确性保证。
|
|
201
|
+
- 执行条件:仅当当前 pane occupant 已被 Herdr 识别(live)且没有合法 Agent Name 时,才对其执行一次 `agent rename <self-pane> <generated>`;已有合法名则原样保留,绝不改写。
|
|
202
|
+
- 生成名规则:Link 专属前缀 `hl-` + 随机十六进制后缀,整体满足 Agent Name 规则 `[a-z][a-z0-9_-]{0,31}`。
|
|
203
|
+
- 确认方式:rename 成功后必须重新读取 authoritative live record 确认命名生效;确认失败即收敛为 `SELF_UNNAMED`。
|
|
204
|
+
- 内部重试仅两类且均为小预算:① 初始探测的就绪重试(仅限「尚未被检测」状态);② CLI 返回 `agent_name_taken` 时的重生重试(确定性业务冲突,非时序猜测)。其余任何失败不做自动 retry。
|
|
205
|
+
- 并发去重:同一时刻至多一个 bootstrap 序列在执行(in-flight 合并),Adapter 启动自举与通信路径兜底不会并发 rename;守卫在结算后即清除,不缓存、不持久化任何名字。
|
|
206
|
+
- 边界:纯 Adapter 内部机制,不向模型暴露任何 rename/claim 工具,Communication Contract 不新增指令;错误文案不得包含 raw pane ID 或 CLI 诊断。
|
|
207
|
+
|
|
208
|
+
## 7. 错误模型
|
|
209
|
+
|
|
210
|
+
V1 定义最小错误语义,全部是本地 tool operation failure,不是跨 Agent message type(不建立 Error Envelope)。
|
|
211
|
+
|
|
212
|
+
| Code | Meaning |
|
|
213
|
+
|---|---|
|
|
214
|
+
| `NOT_IN_HERDR` | Herdr 环境不可用:环境变量缺失、`HERDR_BIN_PATH` 失效/被删除(spawn ENOENT)、CLI transport 失败、响应非法 JSON |
|
|
215
|
+
| `SELF_UNNAMED` | Herdr Link 已尝试建立当前 Agent 的稳定 Agent Name(§6.3)但失败——包括 occupant 尚未被 Herdr 检测、自动命名最终未成功两种情况 |
|
|
216
|
+
| `PEER_NOT_FOUND` | 目标不是当前 workspace 内的 live named peer——涵盖名字非法、目标不存在、目标属于其他 workspace、target workspace 未上报;四种情况对模型不可区分(scope privacy) |
|
|
217
|
+
| `SEND_FAILED` | self 与目标均已解析并通过 guard,但 Herdr 未接受 message prompt |
|
|
218
|
+
| `CLOSE_FAILED` | 目标已解析到 authoritative pane,但 Herdr `pane close` 失败 |
|
|
219
|
+
|
|
220
|
+
### Failure Policy
|
|
221
|
+
|
|
222
|
+
- 不自动 retry;不 fallback 到 terminal send/read;不 fallback 到 focused pane;不转换成 Workflow 状态;
|
|
223
|
+
- **错误分类透传**:底层已归类为 `NOT_IN_HERDR` 的环境/transport 失败,不得被外层操作逻辑重包装成 `SEND_FAILED` / `CLOSE_FAILED` 等操作级错误码;操作逻辑只对尚未分类的意外异常使用自身 fallback 码;
|
|
224
|
+
- Agent-facing tool error 只返回稳定 error code 与简化原因;不得暴露 raw pane/tab/workspace/terminal ID,也不得把 `agent rename` 或其他越界恢复动作提示给模型。底层诊断细节如需保留,只能进入 operator-facing 日志。
|
|
225
|
+
|
|
226
|
+
## 8. Command Safety
|
|
227
|
+
|
|
228
|
+
- Herdr CLI 必须通过 argv 数组执行(`execFile` 或等价无 shell 方式),禁止构造 shell command string。
|
|
229
|
+
- 调用面:`agent get <target>`、`agent list`、`agent prompt <target> <text>`、`pane close <pane_id>`。
|
|
230
|
+
- `agent prompt` 的投递文本是 §2 定义的 self-describing inbound wrapper;除此之外不构造任何额外协议负载。
|
|
231
|
+
- 不使用 `--wait`(V1 无订阅、无等待语义)。不调用:`agent.wait`、`agent.read`、`pane.read`、`events.subscribe`、`pane.send_text`、`pane.send_keys`、`agent start`、任何 workspace 控制命令。
|
|
232
|
+
- `agent rename` 仅限 §6.3 self identity bootstrap 使用:目标只能是当前 pane 中未命名的 live occupant;禁止将其暴露为模型工具、用于任何其他 pane/agent 目标,或在面向模型的文本中提示该动作。
|
|
233
|
+
|
|
234
|
+
## 9. Non-goals
|
|
235
|
+
|
|
236
|
+
V1 不提供:agent 创建/启动/配置/调度、通用 Agent Name 管理(分配策略/持久化/恢复——§6.3 的一次性 self identity bootstrap 除外,Link 自身不持久化任何名字)、自动回收策略、模型选择、workflow/task/stage 状态、业务结果 schema、evidence/receipt/review、持久消息队列、跨机器传输、权限审批系统、离线投递、可靠投递确认、全局权限、跨 session 持久化。
|
|
237
|
+
|
|
238
|
+
明确不属于 Herdr Link 的还有:
|
|
239
|
+
|
|
240
|
+
- **跨 workspace 的 peer discovery / send / close**:属于官方 Herdr Skill / CLI 的高级控制面;
|
|
241
|
+
- **官方 Herdr Skill 依赖**:正常 Agent-to-Agent 协作只依赖 Adapter 自包含交付的 Contract 与工具;Skill 仅是高级可选控制面,Link 不自动 fallback 到 Skill,也不教模型用 CLI 完成正常 Link 操作;
|
|
242
|
+
- workspace/tab/pane topology 创建、pane move 等控制面操作。
|
|
243
|
+
|
|
244
|
+
`herdr_link_close` 只是执行调用方已经作出的显式关闭决定,不拥有 lifecycle policy。
|
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Herdr Link
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/herdr-link)
|
|
4
|
+
[](https://github.com/LZHcode1986/herdr-link/actions/workflows/ci.yml)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](./package.json)
|
|
7
|
+
|
|
8
|
+
**English** | [简体中文](./README.zh-CN.md)
|
|
9
|
+
|
|
10
|
+
Herdr Link is an on-demand cross-agent interoperability layer running inside Herdr sessions. Agents in the same workspace can discover each other, exchange protocol-typed messages, and close finished panes — through **three tools**, with **zero learning overhead**.
|
|
11
|
+
|
|
12
|
+
Adapters are provided for Pi (native extension), OpenCode (plugin bundle), and any MCP-capable runtime such as Claude Code, Codex, or AGY (shared stdio MCP server). The wire format is the `herdr-link/1` protocol, specified canonically in [`PROTOCOL.md`](./PROTOCOL.md).
|
|
13
|
+
|
|
14
|
+
## Why Herdr Link?
|
|
15
|
+
|
|
16
|
+
The usual way to teach agents cross-agent messaging is to point them at the official Herdr Skill. That works, but it has a recurring cost that scales with every agent and every session:
|
|
17
|
+
|
|
18
|
+
- the agent must **read skill documentation and reason about how to drive the CLI** before any message is exchanged;
|
|
19
|
+
- that reasoning **consumes tokens and adds latency on every use**;
|
|
20
|
+
- usage knowledge is **re-derived by the model** instead of being given to it.
|
|
21
|
+
|
|
22
|
+
Herdr Link removes that step entirely. The adapter hands the model three self-describing tools and injects a compact communication contract automatically:
|
|
23
|
+
|
|
24
|
+
| | Official Herdr Skill route | With Herdr Link |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| What the agent must learn | Skill docs + CLI surface | Nothing — call the tool directly |
|
|
27
|
+
| Before the first message | Usage reasoning (tokens + latency) | One tool call |
|
|
28
|
+
| Context cost while idle | Skill content when loaded | A minimal dormant gateway only |
|
|
29
|
+
| Peer addressing | Re-derived ad hoc | `herdr_link_peers` returns live named agents |
|
|
30
|
+
|
|
31
|
+
In short:
|
|
32
|
+
|
|
33
|
+
- **Fewer tokens.** Nothing to read or figure out. While dormant, the model sees only a tiny `herdr_link` gateway — no contract, no schemas. After activation it gets one short contract, not a manual.
|
|
34
|
+
- **Faster communication.** Discover peers, send a protocol-typed message, or close a pane is a single direct tool call — no multi-step CLI orchestration in between.
|
|
35
|
+
- **Effortless ("zero-reasoning") integration.** Activation is automatic on explicit user intent or on receiving an inbound `herdr-link/1` message; reply correlation (`reply_to`) means the agent never has to invent bookkeeping.
|
|
36
|
+
|
|
37
|
+
## How it works
|
|
38
|
+
|
|
39
|
+
Every runtime exposes the same lazy two-tier surface:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
Agent A → herdr_link {} # activate (idempotent)
|
|
43
|
+
Agent A → herdr_link_send(to="B", ...) # status "sent"
|
|
44
|
+
Agent B → (receives inbound wrapper) herdr_link {} # auto-activation trigger
|
|
45
|
+
Agent B → herdr_link_send(to="A", reply_to=<received id>, ...)
|
|
46
|
+
Anyone → herdr_link_close(agent="worker-a") # in a later tool step after the final send
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- **Dormant tier:** only the `herdr_link` gateway is visible; calling it with `{}` activates the session once (idempotent, in-memory only).
|
|
50
|
+
- **Active tier:** `herdr_link_peers`, `herdr_link_send`, `herdr_link_close`, plus the compact Communication Contract. Every call re-resolves live identity/workspace via Herdr and enforces a same-workspace guard.
|
|
51
|
+
|
|
52
|
+
Herdr Link does not decide what agents should do, and it does not create, schedule, model-select, or recycle agents. It is purely the messaging layer.
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
### Pi (native extension)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pi install git:github.com/LZHcode1986/herdr-link # global
|
|
60
|
+
pi install -l git:github.com/LZHcode1986/herdr-link # project-local
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Manual/dev loading:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
mkdir -p ~/.pi/agent/extensions/herdr-link
|
|
67
|
+
cp src/pi.ts ~/.pi/agent/extensions/herdr-link/index.ts
|
|
68
|
+
cp src/herdr.ts src/protocol.ts ~/.pi/agent/extensions/herdr-link/
|
|
69
|
+
# or: pi --extension /path/to/herdr-link/src/pi.ts
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
After installation the adapter registers the `herdr_link` gateway plus the three Tier 1 tools; Tier 1 starts inactive each session and is enabled (with contract injection) when the model calls `herdr_link {}`.
|
|
73
|
+
|
|
74
|
+
### OpenCode (single-file plugin)
|
|
75
|
+
|
|
76
|
+
OpenCode loads **every file** in its plugin directory as a plugin, so deploy the prebuilt single-file bundle — never loose source files:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install -g herdr-link # or: build from source with npm run build:opencode
|
|
80
|
+
cp "$(npm root -g)/herdr-link/dist/herdr-link.opencode.js" \
|
|
81
|
+
~/.config/opencode/plugins/herdr-link.js
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
OpenCode has no per-session tool toggle API, so the adapter presents a **single-gateway dispatcher**: `{}` activates, then `{"action":"peers"|"send"|"close", ...}` dispatches to the same control layer. The contract is injected into the system prompt of activated sessions only (in-memory per `sessionID`; a server restart returns to dormant).
|
|
85
|
+
|
|
86
|
+
### Claude Code / Codex / AGY (shared stdio MCP server)
|
|
87
|
+
|
|
88
|
+
Runtimes without a native custom-tool API all share the same zero-dependency stdio MCP server, published as this package's `bin`:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx -y herdr-link # starts the MCP server on stdio
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Register it under the namespace `herdr_link` (underscore). Presentation differs by host: Claude Code / Codex expose prefixed tools (`mcp__herdr_link__<tool>`), AGY calls through its native `call_mcp_tool` wrapper — inputs, outputs, and error semantics are identical everywhere. Host-specific registration configs and Tier-0 hint wiring (launcher flags / SessionStart hook / PreInvocation hook) are documented in [`docs/mcp-wiring.md`](./docs/mcp-wiring.md).
|
|
95
|
+
|
|
96
|
+
MCP presentation is lazy too: outside Herdr, `tools/list` returns an empty set; in a Herdr-managed pane, dormant lists only the gateway; activation emits `notifications/tools/list_changed` (hosts that never refresh keep full functionality through gateway action dispatch).
|
|
97
|
+
|
|
98
|
+
## Requirements
|
|
99
|
+
|
|
100
|
+
The runtime process must be started by Herdr in a managed pane:
|
|
101
|
+
|
|
102
|
+
| Variable | Purpose |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `HERDR_ENV=1` | Confirms a Herdr environment |
|
|
105
|
+
| `HERDR_BIN_PATH` | Current Herdr binary path; invalid → `NOT_IN_HERDR` |
|
|
106
|
+
| `HERDR_PANE_ID` | Caller pane, used to resolve self identity and authoritative workspace |
|
|
107
|
+
|
|
108
|
+
- Outside a Herdr pane every adapter is a complete no-op: Pi/OpenCode register nothing, MCP returns an empty tool list.
|
|
109
|
+
- In a Herdr pane while dormant, only the `herdr_link` gateway is visible to the model.
|
|
110
|
+
- **Self identity bootstrap** (PROTOCOL.md §6.3): a manually started agent that is recognized by Herdr but has no valid Agent Name is named automatically with a generated `hl-*` name (`ensureSelfName()` at adapter startup plus a fallback inside every communication path). Existing names are never rewritten, nothing is persisted; if the bootstrap fails the Link errors with `SELF_UNNAMED`.
|
|
111
|
+
- Runtime failures come back as Link errors (`NOT_IN_HERDR` / `SELF_UNNAMED` / `PEER_NOT_FOUND` / `SEND_FAILED` / `CLOSE_FAILED`).
|
|
112
|
+
|
|
113
|
+
## Error model
|
|
114
|
+
|
|
115
|
+
| Code | Meaning |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `NOT_IN_HERDR` | Herdr environment unavailable (missing vars, dead binary, transport failure, invalid JSON) |
|
|
118
|
+
| `SELF_UNNAMED` | Herdr Link attempted to establish a stable Agent Name (self identity bootstrap, PROTOCOL.md §6.3) but failed — occupant not yet detected by Herdr or auto-naming unsuccessful |
|
|
119
|
+
| `PEER_NOT_FOUND` | Target is not a live named peer in the current workspace (nonexistent / invalid name / other workspace — indistinguishable to the model) |
|
|
120
|
+
| `SEND_FAILED` | Herdr did not accept the message prompt although guards passed |
|
|
121
|
+
| `CLOSE_FAILED` | Target resolved to a pane but Herdr's pane close failed |
|
|
122
|
+
|
|
123
|
+
Errors are local tool failures, not inter-agent message types; no auto-retry, no fallback, no pending state.
|
|
124
|
+
|
|
125
|
+
## Development
|
|
126
|
+
|
|
127
|
+
The repository ships everything needed to audit and extend the project (`test/`, `tsconfig.json`, build scripts). The npm package is governed by the `files` allowlist in `package.json`.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm install
|
|
131
|
+
npm run typecheck
|
|
132
|
+
npm test # node --experimental-strip-types --test test/*.test.ts
|
|
133
|
+
npm run build:opencode # dist/herdr-link.opencode.js
|
|
134
|
+
npm run build:mcp # dist/herdr-link.mcp.js
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Layout:
|
|
138
|
+
|
|
139
|
+
```text
|
|
140
|
+
PROTOCOL.md canonical protocol spec (envelope, tiers, contract, semantics, errors)
|
|
141
|
+
src/protocol.ts protocol core: types, envelope/wrapper builders, errors, COMMUNICATION_CONTRACT
|
|
142
|
+
src/herdr.ts Herdr CLI control layer: live identity/workspace resolution, same-workspace guard
|
|
143
|
+
src/pi.ts Pi adapter: gateway + deferred Tier 1 (setActiveTools), post-activation contract injection
|
|
144
|
+
src/opencode.ts OpenCode adapter: single-gateway dispatcher + per-sessionID contract injection
|
|
145
|
+
src/mcp.ts shared stdio MCP server: JSON-RPC, lazy tool list, gateway dispatch
|
|
146
|
+
docs/mcp-wiring.md registration & Tier-0 hint wiring for Claude Code / Codex / AGY
|
|
147
|
+
dist/*.js prebuilt bundles (opencode plugin, MCP server bin)
|
|
148
|
+
scripts/mcp-probe.mjs stdio handshake debugging probe
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Layering rule: `protocol.ts` has zero Herdr IO; `herdr.ts` only drives the Herdr control plane (`execFile` argv arrays, no shell); `pi.ts` / `opencode.ts` / `mcp.ts` only do runtime wiring. Activation state lives in memory per runtime session: never persisted, never restored across sessions.
|
|
152
|
+
|
|
153
|
+
## Non-goals (V1)
|
|
154
|
+
|
|
155
|
+
No agent creation/scheduling/recycling, model selection, workflow/task/stage state, business result schemas, evidence/receipt/review, persistent queues, cross-machine transport, permission approval, offline delivery, reliable-delivery acknowledgements, cross-session persistence, or **cross-workspace discovery/send/close** (that belongs to the official Herdr Skill / CLI control plane), and no workspace/topology management. Put business payloads in the `message` field; Link never interprets their semantics.
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
[MIT](./LICENSE)
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Herdr Link
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/herdr-link)
|
|
4
|
+
[](https://github.com/LZHcode1986/herdr-link/actions/workflows/ci.yml)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](./package.json)
|
|
7
|
+
|
|
8
|
+
[English](./README.md) | **简体中文**
|
|
9
|
+
|
|
10
|
+
Herdr Link 是运行在 Herdr 会话中的跨 Agent 按需互操作层。同一 workspace 内的 Agent 可以互相发现、交换协议化消息、关闭已完成的 pane——只通过 **3 个工具**,**零学习成本**。
|
|
11
|
+
|
|
12
|
+
提供 Pi(原生扩展)、OpenCode(插件 bundle)以及任意支持 MCP 的 Runtime 如 Claude Code / Codex / AGY(共享 stdio MCP server)的 Adapter。线上格式为 `herdr-link/1` 协议,唯一规范见 [`PROTOCOL.md`](./PROTOCOL.md)。
|
|
13
|
+
|
|
14
|
+
## 为什么选择 Herdr Link?
|
|
15
|
+
|
|
16
|
+
让 Agent 学会跨 Agent 通信的常规方式是给它官方 Herdr Skill。这可行,但有一笔随每个 Agent、每个会话不断重复支付的成本:
|
|
17
|
+
|
|
18
|
+
- Agent 必须先**阅读 Skill 文档并思考如何驱动 CLI**,然后才谈得上真正通信;
|
|
19
|
+
- 这些推理过程**每次使用都在消耗 token 并增加延迟**;
|
|
20
|
+
- 使用知识靠模型**反复自行推导**,而不是直接交给它。
|
|
21
|
+
|
|
22
|
+
Herdr Link 把这一步彻底去掉。Adapter 直接给模型 3 个自描述工具,并自动注入一份紧凑的通信契约:
|
|
23
|
+
|
|
24
|
+
| | 官方 Herdr Skill 路线 | 使用 Herdr Link |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| Agent 需要学什么 | Skill 文档 + CLI 用法 | 无需学习——直接调用工具 |
|
|
27
|
+
| 第一条消息之前 | 用法推理(token + 延迟) | 一次工具调用 |
|
|
28
|
+
| 空闲期上下文开销 | 加载时携带 Skill 内容 | 仅一个极小的 dormant gateway |
|
|
29
|
+
| 对端寻址 | 每次临时推导 | `herdr_link_peers` 直接返回 live named agents |
|
|
30
|
+
|
|
31
|
+
一句话总结:
|
|
32
|
+
|
|
33
|
+
- **更少消耗。** 无需阅读、无需推导。dormant 态下模型只看到一个极小的 `herdr_link` gateway——无契约、无 schema;激活后也只注入一段简短契约,而不是一本手册。
|
|
34
|
+
- **更快通讯。** 发现对端、发送协议化消息、关闭 pane 都是一次直接的工具调用——中间没有任何多步 CLI 编排。
|
|
35
|
+
- **无感接入(零推理)。** 用户显式提出 Herdr 需求、或收到 inbound `herdr-link/1` 消息时自动激活;回复通过 `reply_to` 关联,Agent 不需要自己发明簿记机制。
|
|
36
|
+
|
|
37
|
+
## 工作方式
|
|
38
|
+
|
|
39
|
+
每种 Runtime 都呈现同样的惰性两级能力面:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
Agent A → herdr_link {} # 激活(幂等)
|
|
43
|
+
Agent A → herdr_link_send(to="B", ...) # status "sent"
|
|
44
|
+
Agent B → (收到 inbound wrapper)herdr_link {} # 自动激活触发
|
|
45
|
+
Agent B → herdr_link_send(to="A", reply_to=<received id>, ...)
|
|
46
|
+
任意一方 → herdr_link_close(agent="worker-a") # 最终 send 返回 sent 之后的工具步骤
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- **Dormant 层**:只有 `herdr_link` gateway 可见;空参 `{}` 调用一次性激活当前 session(幂等、纯内存态)。
|
|
50
|
+
- **Active 层**:`herdr_link_peers`、`herdr_link_send`、`herdr_link_close`,外加紧凑 Communication Contract。每次调用都经 Herdr 实时解析身份/workspace 并执行同 workspace guard。
|
|
51
|
+
|
|
52
|
+
Herdr Link 不决定 Agent 应该做什么,也不负责 Agent 的创建、调度、模型选择或回收——它只是消息层。
|
|
53
|
+
|
|
54
|
+
## 安装
|
|
55
|
+
|
|
56
|
+
### Pi(原生扩展)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pi install git:github.com/LZHcode1986/herdr-link # 全局
|
|
60
|
+
pi install -l git:github.com/LZHcode1986/herdr-link # 仅当前项目
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
手动/开发加载:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
mkdir -p ~/.pi/agent/extensions/herdr-link
|
|
67
|
+
cp src/pi.ts ~/.pi/agent/extensions/herdr-link/index.ts
|
|
68
|
+
cp src/herdr.ts src/protocol.ts ~/.pi/agent/extensions/herdr-link/
|
|
69
|
+
# 或:pi --extension /path/to/herdr-link/src/pi.ts
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
安装后 Adapter 注册 `herdr_link` gateway 与三个 Tier 1 工具;每个 session 开始时 Tier 1 处于 inactive,模型调用 `herdr_link {}` 后启用并注入契约。
|
|
73
|
+
|
|
74
|
+
### OpenCode(单文件插件)
|
|
75
|
+
|
|
76
|
+
OpenCode 把插件目录里**每个文件**都当作 plugin 加载,因此必须部署预构建的单文件 bundle——绝不能平铺源文件:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install -g herdr-link # 或源码构建:npm run build:opencode
|
|
80
|
+
cp "$(npm root -g)/herdr-link/dist/herdr-link.opencode.js" \
|
|
81
|
+
~/.config/opencode/plugins/herdr-link.js
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
OpenCode 没有按 session 启停工具的 API,因此 Adapter 采用**single-gateway dispatcher** 呈现:`{}` 激活,之后 `{"action":"peers"|"send"|"close", ...}` 分发到同一控制层。契约只注入已激活 session 的 system prompt(按 `sessionID` 记忆的内存态;server 重启回到 dormant)。
|
|
85
|
+
|
|
86
|
+
### Claude Code / Codex / AGY(共享 stdio MCP server)
|
|
87
|
+
|
|
88
|
+
没有原生自定义工具注册面的 Runtime 共用同一个零依赖 stdio MCP server,以本包的 `bin` 发布:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npx -y herdr-link # 在 stdio 上启动 MCP server
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
注册 namespace 必须用 `herdr_link`(下划线)。各 host 呈现形态不同:Claude Code / Codex 以前缀函数(`mcp__herdr_link__<tool>`)呈现,AGY 经原生 `call_mcp_tool` wrapper 调用——入参、出参与错误语义完全一致。各 host 的注册配置与 Tier-0 hint 接线(launcher 参数 / SessionStart hook / PreInvocation hook)见 [`docs/mcp-wiring.md`](./docs/mcp-wiring.md)。
|
|
95
|
+
|
|
96
|
+
MCP 同样是惰性呈现:非 Herdr 环境 `tools/list` 返回空集;Herdr managed pane 内 dormant 时只列出 gateway;激活后发射一次 `notifications/tools/list_changed`(不响应刷新的 host 可继续通过 gateway action 分发保持全功能)。
|
|
97
|
+
|
|
98
|
+
## 环境要求
|
|
99
|
+
|
|
100
|
+
运行进程必须由 Herdr 在 managed pane 中启动:
|
|
101
|
+
|
|
102
|
+
| 变量 | 用途 |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `HERDR_ENV=1` | 确认处于 Herdr 环境 |
|
|
105
|
+
| `HERDR_BIN_PATH` | 当前 Herdr binary 路径;失效时返回 `NOT_IN_HERDR` |
|
|
106
|
+
| `HERDR_PANE_ID` | caller pane,用于实时解析 self identity 与权威 workspace |
|
|
107
|
+
|
|
108
|
+
- 非 Herdr managed pane 中所有 Adapter 均为完全 no-op:Pi/OpenCode 不注册任何工具,MCP 返回空工具集;
|
|
109
|
+
- Herdr 环境 dormant 态下,模型侧只有 `herdr_link` gateway 可见;
|
|
110
|
+
- **Self identity bootstrap**(PROTOCOL.md §6.3):用户手动启动、已被 Herdr 识别但尚无合法 Agent Name 的 agent,会被自动赋一个生成的 `hl-*` 名字(Adapter 启动时执行一次 `ensureSelfName()`,通信路径内另有 fallback)。已有名字绝不改写、不持久化;bootstrap 失败时 Link 以 `SELF_UNNAMED` 报错;
|
|
111
|
+
- 运行期失败通过 Link error 返回(`NOT_IN_HERDR` / `SELF_UNNAMED` / `PEER_NOT_FOUND` / `SEND_FAILED` / `CLOSE_FAILED`)。
|
|
112
|
+
|
|
113
|
+
## 错误模型
|
|
114
|
+
|
|
115
|
+
| Code | 含义 |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `NOT_IN_HERDR` | Herdr 环境不可用(变量缺失、binary 失效/被删除、transport 失败、非法 JSON) |
|
|
118
|
+
| `SELF_UNNAMED` | Herdr Link 已尝试建立稳定 Agent Name(self identity bootstrap,PROTOCOL.md §6.3)但失败——occupant 尚未被 Herdr 检测或自动命名未成功 |
|
|
119
|
+
| `PEER_NOT_FOUND` | 目标不是当前 workspace 内的 live named peer(不存在/非法名/其他 workspace——对模型不可区分) |
|
|
120
|
+
| `SEND_FAILED` | guard 通过后 Herdr 未接受 message prompt |
|
|
121
|
+
| `CLOSE_FAILED` | 目标已解析到 pane,但 Herdr pane close 失败 |
|
|
122
|
+
|
|
123
|
+
错误是本地 tool failure,不是跨 Agent 消息类型;不自动重试、不 fallback、不维护 pending 状态。
|
|
124
|
+
|
|
125
|
+
## 开发
|
|
126
|
+
|
|
127
|
+
仓库包含完整的可审计与可扩展组件(`test/`、`tsconfig.json`、构建脚本)。npm 发布包由 `package.json` 的 `files` allowlist 控制。
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm install
|
|
131
|
+
npm run typecheck
|
|
132
|
+
npm test # node --experimental-strip-types --test test/*.test.ts
|
|
133
|
+
npm run build:opencode # dist/herdr-link.opencode.js
|
|
134
|
+
npm run build:mcp # dist/herdr-link.mcp.js
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
目录结构:
|
|
138
|
+
|
|
139
|
+
```text
|
|
140
|
+
PROTOCOL.md 协议唯一规范(Envelope、两级能力面、Contract、工具语义、错误模型)
|
|
141
|
+
src/protocol.ts 协议核心:类型、envelope/wrapper 构建、错误、COMMUNICATION_CONTRACT
|
|
142
|
+
src/herdr.ts Herdr CLI 控制层:live identity/workspace 解析、same-workspace guard
|
|
143
|
+
src/pi.ts Pi Runtime Adapter:gateway + deferred Tier 1(setActiveTools),激活后注入契约
|
|
144
|
+
src/opencode.ts OpenCode Runtime Adapter:single-gateway dispatcher + 按 sessionID 契约注入
|
|
145
|
+
src/mcp.ts 共享 stdio MCP server:JSON-RPC、惰性工具列表、gateway dispatch
|
|
146
|
+
docs/mcp-wiring.md Claude Code / Codex / AGY 注册与 Tier-0 hint 接线指南
|
|
147
|
+
dist/*.js 预构建 bundle(opencode 插件、MCP server bin)
|
|
148
|
+
scripts/mcp-probe.mjs stdio 握手排障探针
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
分层原则:`protocol.ts` 零 Herdr IO;`herdr.ts` 只做 Herdr 控制面调用(`execFile` argv 数组,无 shell);`pi.ts` / `opencode.ts` / `mcp.ts` 各自只做 Runtime 接线。activation 是各 Adapter 内存中的 session 局部状态:不持久化、不跨 session 恢复。
|
|
152
|
+
|
|
153
|
+
## Non-goals(V1)
|
|
154
|
+
|
|
155
|
+
不提供:agent 创建/调度/回收、模型选择、workflow/task/stage 状态、业务结果 schema、evidence/receipt/review、持久消息队列、跨机器传输、权限审批、离线投递、可靠投递确认、跨 session 持久化、**跨 workspace 的 discovery/send/close**(属官方 Herdr Skill / CLI 控制面)、workspace/topology 管理面操作。业务 payload 放入 `message` 字段,Link 不解释其语义。
|
|
156
|
+
|
|
157
|
+
## 许可证
|
|
158
|
+
|
|
159
|
+
[MIT](./LICENSE)
|