herdr-link 0.3.0 → 0.4.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 +19 -0
- package/PROTOCOL.md +40 -20
- package/README.md +116 -12
- package/README.zh-CN.md +116 -12
- package/dist/herdr-link.mcp.js +225 -17
- package/dist/herdr-link.opencode.js +207 -11
- package/docs/mcp-wiring.md +19 -16
- package/examples/agent_config.example.json +41 -0
- package/package.json +3 -2
- package/src/herdr.ts +231 -0
- package/src/mcp.ts +55 -26
- package/src/opencode.ts +43 -11
- package/src/pi.ts +46 -11
- package/src/protocol.ts +50 -3
package/src/pi.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Tier 0 (dormant): with the three Herdr environment variables present, the
|
|
5
5
|
* adapter registers everything but keeps the model-facing surface down to the
|
|
6
|
-
* tiny `herdr_link` gateway. The
|
|
7
|
-
* (`herdr_link_peers`/`herdr_link_send`/`herdr_link_close`) stay inactive and
|
|
6
|
+
* tiny `herdr_link` gateway. The four Tier 1 tools
|
|
7
|
+
* (`herdr_link_start`/`herdr_link_peers`/`herdr_link_send`/`herdr_link_close`) stay inactive and
|
|
8
8
|
* no Communication Contract is injected.
|
|
9
9
|
*
|
|
10
10
|
* Tier 1 (active): calling the gateway with `{}` idempotently activates the
|
|
@@ -27,13 +27,27 @@
|
|
|
27
27
|
import type { ExtensionAPI, ToolExecutionMode } from "@earendil-works/pi-coding-agent";
|
|
28
28
|
import { Type } from "typebox";
|
|
29
29
|
|
|
30
|
-
import { closeAgentPane, ensureSelfName, listPeers, sendMessage } from "./herdr.ts";
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
import { closeAgentPane, ensureSelfName, listPeers, sendMessage, startAgent } from "./herdr.ts";
|
|
31
|
+
import {
|
|
32
|
+
COMMUNICATION_CONTRACT,
|
|
33
|
+
HERDR_LINK_TOOLS,
|
|
34
|
+
START_TOOL_DESCRIPTION,
|
|
35
|
+
TOOL_START,
|
|
36
|
+
type StartAgentInput,
|
|
37
|
+
formatAgentFacingError,
|
|
38
|
+
} from "./protocol.ts";
|
|
39
|
+
|
|
40
|
+
const TIER1_TOOL_NAMES = HERDR_LINK_TOOLS;
|
|
34
41
|
const TIER1_TOOL_SET = new Set<string>(TIER1_TOOL_NAMES);
|
|
35
42
|
|
|
36
43
|
const GATEWAY_PARAMETERS = Type.Object({});
|
|
44
|
+
const START_PARAMETERS = Type.Object({
|
|
45
|
+
name: Type.String(),
|
|
46
|
+
pane: Type.String(),
|
|
47
|
+
config_agent: Type.Optional(Type.String()),
|
|
48
|
+
kind: Type.Optional(Type.String()),
|
|
49
|
+
args: Type.Optional(Type.Array(Type.String())),
|
|
50
|
+
});
|
|
37
51
|
const PEERS_PARAMETERS = Type.Object({});
|
|
38
52
|
const SEND_PARAMETERS = Type.Object({
|
|
39
53
|
to: Type.String(),
|
|
@@ -51,7 +65,14 @@ function toolResult(value: object) {
|
|
|
51
65
|
};
|
|
52
66
|
}
|
|
53
67
|
|
|
54
|
-
function rethrowToolError(
|
|
68
|
+
function rethrowToolError(
|
|
69
|
+
error: unknown,
|
|
70
|
+
fallbackCode:
|
|
71
|
+
| "NOT_IN_HERDR"
|
|
72
|
+
| "START_FAILED"
|
|
73
|
+
| "SEND_FAILED"
|
|
74
|
+
| "CLOSE_FAILED",
|
|
75
|
+
): never {
|
|
55
76
|
const toolError = new Error(formatAgentFacingError(error, fallbackCode), { cause: error });
|
|
56
77
|
throw toolError;
|
|
57
78
|
}
|
|
@@ -73,6 +94,20 @@ export default function (pi: ExtensionAPI): void {
|
|
|
73
94
|
// setActiveTools), kept initially inactive by the session_start hook below.
|
|
74
95
|
// Their descriptions alone carry the canonical affordances; prompt metadata
|
|
75
96
|
// is intentionally omitted (see module doc).
|
|
97
|
+
pi.registerTool({
|
|
98
|
+
name: TOOL_START,
|
|
99
|
+
label: "Herdr Link Start",
|
|
100
|
+
description: START_TOOL_DESCRIPTION,
|
|
101
|
+
parameters: START_PARAMETERS,
|
|
102
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
103
|
+
try {
|
|
104
|
+
return toolResult(await startAgent(params as StartAgentInput, { cwd: ctx.cwd }));
|
|
105
|
+
} catch (error) {
|
|
106
|
+
rethrowToolError(error, "START_FAILED");
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
76
111
|
pi.registerTool({
|
|
77
112
|
name: "herdr_link_peers",
|
|
78
113
|
label: "Herdr Link Peers",
|
|
@@ -125,17 +160,17 @@ export default function (pi: ExtensionAPI): void {
|
|
|
125
160
|
});
|
|
126
161
|
|
|
127
162
|
// --- Tier 0 gateway: the only model-visible Herdr surface while dormant.
|
|
128
|
-
// It performs activation only; it never executes peers/send/close work.
|
|
163
|
+
// It performs activation only; it never executes start/peers/send/close work.
|
|
129
164
|
pi.registerTool({
|
|
130
165
|
name: "herdr_link",
|
|
131
166
|
label: "Herdr Link",
|
|
132
167
|
description:
|
|
133
|
-
"Activate the Herdr Link channel only when the user explicitly asks to use Herdr or when handling an inbound Herdr Link message. Call once with empty arguments {} before using Herdr Link; this enables herdr_link_peers, herdr_link_send, and herdr_link_close.",
|
|
134
|
-
promptSnippet: "Activate the Herdr Link cross-agent channel (peers/send/close).",
|
|
168
|
+
"Activate the Herdr Link channel only when the user explicitly asks to use Herdr or when handling an inbound Herdr Link message. Call once with empty arguments {} before using Herdr Link; this enables herdr_link_start, herdr_link_peers, herdr_link_send, and herdr_link_close.",
|
|
169
|
+
promptSnippet: "Activate the Herdr Link cross-agent channel (start/peers/send/close).",
|
|
135
170
|
parameters: GATEWAY_PARAMETERS,
|
|
136
171
|
async execute(_toolCallId, _params, _signal, _onUpdate, _ctx) {
|
|
137
172
|
activateChannel();
|
|
138
|
-
return toolResult({ status: "active", capabilities: ["peers", "send", "close"] });
|
|
173
|
+
return toolResult({ status: "active", capabilities: ["start", "peers", "send", "close"] });
|
|
139
174
|
},
|
|
140
175
|
});
|
|
141
176
|
|
package/src/protocol.ts
CHANGED
|
@@ -20,17 +20,19 @@ export const MESSAGE_ID_RE = /^hl_[a-z0-9]+_[a-z0-9]+$/;
|
|
|
20
20
|
* docs/mcp-wiring.md). Tier 1 are the canonical tool names exposed
|
|
21
21
|
* through the gateway. Both are stable machine-usable constants;
|
|
22
22
|
* runtime-specific presented names must map deterministically onto them
|
|
23
|
-
* (PROTOCOL.md §4.
|
|
23
|
+
* (PROTOCOL.md §4.6).
|
|
24
24
|
* ------------------------------------------------------------------ */
|
|
25
25
|
|
|
26
26
|
/** Tier 0 — gateway name in its underscore host-namespace form. */
|
|
27
27
|
export const HERDR_LINK_GATEWAY = "herdr_link" as const;
|
|
28
28
|
|
|
29
29
|
/** Tier 1 — canonical tool names exposed through the gateway. */
|
|
30
|
+
export const TOOL_START = "herdr_link_start" as const;
|
|
30
31
|
export const TOOL_PEERS = "herdr_link_peers" as const;
|
|
31
32
|
export const TOOL_SEND = "herdr_link_send" as const;
|
|
32
33
|
export const TOOL_CLOSE = "herdr_link_close" as const;
|
|
33
|
-
export const HERDR_LINK_TOOLS = [TOOL_PEERS, TOOL_SEND, TOOL_CLOSE] as const;
|
|
34
|
+
export const HERDR_LINK_TOOLS = [TOOL_START, TOOL_PEERS, TOOL_SEND, TOOL_CLOSE] as const;
|
|
35
|
+
export const HERDR_LINK_COMMUNICATION_TOOLS = [TOOL_PEERS, TOOL_SEND, TOOL_CLOSE] as const;
|
|
34
36
|
|
|
35
37
|
/* ------------------------------------------------------------------ *
|
|
36
38
|
* Agent state (blueprint v2)
|
|
@@ -83,6 +85,41 @@ export interface AgentContext {
|
|
|
83
85
|
agent_status: AgentState;
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
/** Common input for the Agent start execution primitive. */
|
|
89
|
+
export interface StartCommonInput {
|
|
90
|
+
/** New Herdr Agent Name. */
|
|
91
|
+
name: string;
|
|
92
|
+
/** Existing pane in which Herdr starts the Agent. */
|
|
93
|
+
pane: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Project-configured start: the configuration owns the complete launch parameters. */
|
|
97
|
+
export interface ConfiguredStartInput extends StartCommonInput {
|
|
98
|
+
config_agent: string;
|
|
99
|
+
kind?: never;
|
|
100
|
+
args?: never;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Explicit start: the caller owns the complete Herdr launch parameters. */
|
|
104
|
+
export interface ExplicitStartInput extends StartCommonInput {
|
|
105
|
+
kind: string;
|
|
106
|
+
args: string[];
|
|
107
|
+
config_agent?: never;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type StartAgentInput = ConfiguredStartInput | ExplicitStartInput;
|
|
111
|
+
|
|
112
|
+
/** Stable receipt returned after Herdr accepts an Agent start. */
|
|
113
|
+
export interface StartAgentReceipt {
|
|
114
|
+
status: "started";
|
|
115
|
+
agent: string;
|
|
116
|
+
kind: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Shared model-facing description; detailed config semantics live in the start section. */
|
|
120
|
+
export const START_TOOL_DESCRIPTION =
|
|
121
|
+
"Start a new Herdr Agent in an existing pane. Provide name and pane, then choose exactly one complete parameter source: config_agent for .agents/agent_config.json, or kind plus args for explicit Herdr start parameters. These modes are mutually exclusive; partial overrides are not supported. This operation does not create panes or retry/fallback after failure.";
|
|
122
|
+
|
|
86
123
|
/** The cross-agent message envelope (PROTOCOL.md §2). Minimal fields only. */
|
|
87
124
|
export interface HerdrLinkEnvelope {
|
|
88
125
|
protocol: typeof PROTOCOL_ID;
|
|
@@ -99,6 +136,11 @@ export const LINK_ERROR_CODES = [
|
|
|
99
136
|
"PEER_NOT_FOUND",
|
|
100
137
|
"SEND_FAILED",
|
|
101
138
|
"CLOSE_FAILED",
|
|
139
|
+
"START_CONFIG_NOT_FOUND",
|
|
140
|
+
"START_AGENT_NOT_FOUND",
|
|
141
|
+
"START_CONFIG_INVALID",
|
|
142
|
+
"START_INPUT_INVALID",
|
|
143
|
+
"START_FAILED",
|
|
102
144
|
] as const;
|
|
103
145
|
|
|
104
146
|
export type LinkErrorCode = (typeof LINK_ERROR_CODES)[number];
|
|
@@ -121,6 +163,11 @@ export const AGENT_ERROR_DETAILS: Record<LinkErrorCode, string> = {
|
|
|
121
163
|
PEER_NOT_FOUND: "target agent is not a live peer",
|
|
122
164
|
SEND_FAILED: "Herdr did not accept message delivery",
|
|
123
165
|
CLOSE_FAILED: "Herdr pane close failed",
|
|
166
|
+
START_CONFIG_NOT_FOUND: "configured Agent start configuration was not found",
|
|
167
|
+
START_AGENT_NOT_FOUND: "configured Agent start entry was not found",
|
|
168
|
+
START_CONFIG_INVALID: "configured Agent start configuration is invalid",
|
|
169
|
+
START_INPUT_INVALID: "Agent start input is invalid",
|
|
170
|
+
START_FAILED: "Herdr did not accept Agent start",
|
|
124
171
|
};
|
|
125
172
|
|
|
126
173
|
/** Formats a Link failure without exposing raw Herdr topology or CLI details. */
|
|
@@ -259,7 +306,7 @@ export function extractInboundEnvelope(text: string): HerdrLinkEnvelope | undefi
|
|
|
259
306
|
*/
|
|
260
307
|
export const COMMUNICATION_CONTRACT = `Herdr Link is the standard interoperability channel between agents running in the same Herdr workspace.
|
|
261
308
|
|
|
262
|
-
1. Use herdr_link_peers
|
|
309
|
+
1. Use herdr_link_peers only for agent-address discovery or explicit recovery. Its activity state is advisory and must not be used to wait for or infer task completion. When further progress depends on a peer reply, end the current turn and continue when that reply arrives as a new inbound herdr-link/1 message.
|
|
263
310
|
2. Use herdr_link_send to send messages to another agent.
|
|
264
311
|
3. A message with protocol "herdr-link/1" is an inter-agent message.
|
|
265
312
|
4. Treat its "message" field as content sent by the agent named in "from".
|