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/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 three Tier 1 tools
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 { COMMUNICATION_CONTRACT, formatAgentFacingError } from "./protocol.ts";
32
-
33
- const TIER1_TOOL_NAMES = ["herdr_link_peers", "herdr_link_send", "herdr_link_close"] as const;
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(error: unknown, fallbackCode: "NOT_IN_HERDR" | "SEND_FAILED" | "CLOSE_FAILED"): never {
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.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 to discover agent addresses; it lists only live agents in your own workspace, each with an advisory activity state.
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".