@byokit/herdr 0.7.1 → 0.7.3
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 +23 -0
- package/README.md +19 -10
- package/dist/agents.d.ts +3 -2
- package/dist/agents.js +38 -5
- package/dist/device.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/kit.d.ts +3 -0
- package/dist/kit.js +1 -0
- package/dist/link.d.ts +1 -1
- package/dist/link.js +2 -2
- package/dist/testing/contract.js +30 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.7.3 (2026-10-11)
|
|
6
|
+
|
|
7
|
+
- Dependency update: pins @byokit/discover 0.7.0.
|
|
8
|
+
|
|
9
|
+
- `@byokit/herdr` exports `isPromptable(agent)`, the single readiness gate `prompt` already used, and adds
|
|
10
|
+
`kit.waitPromptable(target, { timeoutMs })`: it waits until a just-started agent can take a prompt instead of the
|
|
11
|
+
host polling `prompt` and catching `agent-not-ready`. It resolves once the kit's snapshot is promptable, rejects
|
|
12
|
+
`agent-not-ready` at the deadline, and rejects `pane-unavailable` when the pane closes mid-wait.
|
|
13
|
+
- The README's rename note and examples now point at `@byokit/pair` (the phone hand-off host) and the
|
|
14
|
+
example screenshots were re-captured from today's herdr-kit phone page. Docs only; no typed surface changed.
|
|
15
|
+
- Dependency update: depend on `@byokit/pair` 0.9.0 instead of its deprecated `@byokit/link` shim.
|
|
16
|
+
|
|
17
|
+
## 0.7.2 (2026-10-09)
|
|
18
|
+
|
|
19
|
+
- Dependency update: pins @byokit/relay 0.6.0.
|
|
20
|
+
- Dependency update: pins @byokit/link 0.8.0.
|
|
21
|
+
- FIX: (from @byokit/pair 0.9.0) a phone killed while the person at the computer decides now resumes against the pinned computer, for compact and version 1 QRs alike. `pairWithOffer` takes `onPending(grant)`, which hands over the pending grant to keep before the computer can approve this device, and `pendingGrant` accepts a compact QR given the authenticated `host` key, and `parseOffer` reads a compact QR for inspection (expiry, addresses, name, role) without one. Before, `pendingGrant` refused compact QRs, and even a version 1 pending grant never came online after a real kill. A socket that drops once the computer has taken the code now ends pairing with `unreachable` instead of trying the next address, where the spent code or ticket could only be refused as `wrong-code` or `expired`. A socket that closes while `onPending` is still saving ends pairing at once with that close; a version 1 `onPending` that never settles times out. The compact QR is unchanged: 109 characters for one relay address and the name Umer.
|
|
22
|
+
- SECURITY: (from @byokit/pair 0.9.0) pinning and approval semantics. A compact QR's pending grant pins the key the code handshake authenticated, kept after Noise message 2 and before the device's identity goes out in message 3; if saving it fails, pairing stops before the computer learns the device. A device using `onPending` says so inside the handshake, and only then does the computer keep a yes given after its socket dropped, granting only the device key that handshake authenticated; any other device that leaves still gets no grant. A computer with another key at the same address is still refused, and a version 1 `pendingGrant` refuses a `host` that differs from its QR.
|
|
23
|
+
- FIX: (from @byokit/pair 0.9.0) the pairing picture's words match what the host now prints: scan the code, or type the shown code where the phone page asks for it, with no raw address to type.
|
|
24
|
+
- FIX: (from @byokit/pair 0.9.0) the browser pairing test closes only the Chromium processes it spawned — over that browser's own private CDP pipe — instead of signalling a whole process group, so a killed or timed-out test can no longer take out or leave behind the wrong process tree. `BYOKIT_CHROME` is the single documented key that selects the test browser.
|
|
25
|
+
|
|
26
|
+
- The herdr-kit example's agent rows lead with the workspace folder and name the agent second (`shop · Pi`), question cards name folder and agent without repeating the computer, and state badges read without the sentence's full stop (`Ready for you`).
|
|
27
|
+
|
|
5
28
|
## 0.7.1 (2026-10-07)
|
|
6
29
|
|
|
7
30
|
- Dependency update: pins @byokit/relay 0.5.3.
|
package/README.md
CHANGED
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
<p align="center"><strong>Drive the Herdr already on this computer from an app, or from a phone.</strong><br/>
|
|
10
10
|
Workspaces, tabs, panes, coding agents and blocked-approval answers, from a Node app or handed to a phone over
|
|
11
|
-
<a href="../
|
|
11
|
+
<a href="../pair"><code>@byokit/pair</code></a>. Each agent inside Herdr keeps its own subscription login; the kit
|
|
12
12
|
never sees a credential.</p>
|
|
13
13
|
|
|
14
|
+
<p align="center"><sub>This kit depends on <a href="../pair"><code>@byokit/pair</code></a>, and its examples
|
|
15
|
+
import from there.</sub></p>
|
|
16
|
+
|
|
14
17
|
<p align="center">
|
|
15
|
-
<img src="https://raw.githubusercontent.com/umeranjum17/byokit/main/examples/herdr-kit/docs/3-agent.png" width="240" alt="Phone page titled Agents, Connected to Kitchen computer
|
|
16
|
-
<img src="https://raw.githubusercontent.com/umeranjum17/byokit/main/examples/herdr-kit/docs/4-question.png" width="240" alt="Phone page with Questions for you at the top:
|
|
17
|
-
<img src="https://raw.githubusercontent.com/umeranjum17/byokit/main/examples/herdr-kit/docs/5-answered.png" width="240" alt="Phone page after the answer: no Questions for you section, both
|
|
18
|
+
<img src="https://raw.githubusercontent.com/umeranjum17/byokit/main/examples/herdr-kit/docs/3-agent.png" width="240" alt="Phone page titled Agents, Connected to Kitchen computer - same Wi-Fi.: two Pi agents marked Ready for you, and under Talk to it the agent's screen reading '> Add a --json flag to export', 'Read src/cli/export.ts', 'Edited src/cli/export.ts +18 -3' and 'export --json now prints JSON.' above a Message box, a Send button and Waiting for the reply below." />
|
|
19
|
+
<img src="https://raw.githubusercontent.com/umeranjum17/byokit/main/examples/herdr-kit/docs/4-question.png" width="240" alt="Phone page with Questions for you at the top: Pi, Waiting for your answer., the question 'Allow this? (y/n)' and buttons Allow, Deny and Skip; below, the second Pi agent marked Waiting for your answer." />
|
|
20
|
+
<img src="https://raw.githubusercontent.com/umeranjum17/byokit/main/examples/herdr-kit/docs/5-answered.png" width="240" alt="Phone page after the answer: no Questions for you section, both Pi agents marked Ready for you, and the agent's screen ending in 'Allow this? (y/n)', 'y' and 'npm test: 42 passing'" />
|
|
18
21
|
</p>
|
|
19
22
|
<p align="center"><sub>The <a href="../../examples/herdr-kit">herdr-kit example</a>'s phone page: an agent at work, its
|
|
20
23
|
question, answered. Captured by the example's e2e in a phone-sized headless Chromium, against the kit's stand-in
|
|
@@ -90,7 +93,7 @@ Waiting for your answer. | Allow this? (y/n)
|
|
|
90
93
|
idle
|
|
91
94
|
```
|
|
92
95
|
|
|
93
|
-
To hand Herdr to a phone, spread `herdrLink` into a `@byokit/
|
|
96
|
+
To hand Herdr to a phone, spread `herdrLink` into a `@byokit/pair` host and serve it; the phone calls the same
|
|
94
97
|
things through `herdrDevice`:
|
|
95
98
|
|
|
96
99
|
```ts
|
|
@@ -106,8 +109,8 @@ const own = new HerdrKit({ mode: 'own', bin, stateDir: './.state' });
|
|
|
106
109
|
```ts
|
|
107
110
|
import { HerdrKit } from '@byokit/herdr';
|
|
108
111
|
import { herdrLink, serve } from '@byokit/herdr/link';
|
|
109
|
-
import { Host, type Grant } from '@byokit/
|
|
110
|
-
import { hostKeyFile } from '@byokit/
|
|
112
|
+
import { Host, type Grant } from '@byokit/pair';
|
|
113
|
+
import { hostKeyFile } from '@byokit/pair/node';
|
|
111
114
|
|
|
112
115
|
const kit = new HerdrKit({ mode: 'own', bin: '/usr/local/bin/herdr', stateDir: './.state' });
|
|
113
116
|
const grants: Grant[] = [];
|
|
@@ -128,7 +131,7 @@ kit.start().catch(() => {}); // non-fatal: retry later while Herdr is dow
|
|
|
128
131
|
```
|
|
129
132
|
|
|
130
133
|
```ts
|
|
131
|
-
import type { DeviceLink } from '@byokit/
|
|
134
|
+
import type { DeviceLink } from '@byokit/pair';
|
|
132
135
|
import { agentWords, herdrDevice } from '@byokit/herdr/device';
|
|
133
136
|
|
|
134
137
|
declare const link: DeviceLink; // from pairing (pairWithCode / pairWithOffer)
|
|
@@ -153,8 +156,8 @@ kit does not have yet, and [Two ways in](#two-ways-in) plus
|
|
|
153
156
|
|
|
154
157
|
| Export | What it does |
|
|
155
158
|
|---|---|
|
|
156
|
-
| `HerdrKit` (`@byokit/herdr`) | The host-side kit, in `adopt` or `own` mode: `start`/`stop`, `state`, `snapshot`/`onChange`, `startAgent`, `openSignInTab`, `move`, `moveToAccount`, `onStartAgent`, `prompt`, `sendKeys`, `wait`, `read`, `blocked`/`onBlocked`/`answer`, `closePane`/`closeTab`/`closeWorkspace`, `agentKinds`, `installedAgentKinds`, `agentStatus`, `agentInstallState`, `terminal`, `onEvent`, `statusWatchReady`, and the pass-throughs `call`, `subscribe` and `cli` |
|
|
157
|
-
| `agentStatus`, `agentInstallState`, `agentProbePath`, `extraPathDirs`, `runStatusCommand`, `isAutoInstallShim`, `resolveAgentBinary` (`@byokit/herdr`) |
|
|
159
|
+
| `HerdrKit` (`@byokit/herdr`) | The host-side kit, in `adopt` or `own` mode: `start`/`stop`, `state`, `snapshot`/`onChange`, `startAgent`, `openSignInTab`, `move`, `moveToAccount`, `onStartAgent`, `prompt`, `waitPromptable`, `sendKeys`, `wait`, `read`, `blocked`/`onBlocked`/`answer`, `closePane`/`closeTab`/`closeWorkspace`, `agentKinds`, `installedAgentKinds`, `agentStatus`, `agentInstallState`, `terminal`, `onEvent`, `statusWatchReady`, and the pass-throughs `call`, `subscribe` and `cli` |
|
|
160
|
+
| `isPromptable`, `agentStatus`, `agentInstallState`, `agentProbePath`, `extraPathDirs`, `runStatusCommand`, `isAutoInstallShim`, `resolveAgentBinary` (`@byokit/herdr`) | `isPromptable(agent)` is the readiness rule `prompt` and `waitPromptable` share; the rest is onboarding readiness: per-kind install + CLI sign-in without a Herdr connection (see below) |
|
|
158
161
|
| `HERDR_VERSION`, `HERDR_PROTOCOL` | The pinned Herdr release (`0.9.1`) and the protocol the kit speaks (`22`) |
|
|
159
162
|
| `words`, `agentWords`, `stateWords`, `WORDS` | Plain sentences for agent statuses and kit states |
|
|
160
163
|
| `herdrLink` (`@byokit/herdr/link`) | The host side of the `hd.*` link ops, spread into `Host.open`; scopes each grant to its workspaces and can push sealed approval notices through a relay |
|
|
@@ -174,6 +177,12 @@ null), `agentSession` is absent; callers can fall back to their existing checks.
|
|
|
174
177
|
Types (`HerdrKitOptions`, `HerdrState`, `StartAgent`, `PromptReceipt`, `BlockedAgent`, `HerdrSnapshot`,
|
|
175
178
|
`HerdrMethod`/`HerdrParams`/`HerdrResult`, `HerdrEventName`/`HerdrEventOf`, ...) come from the main entry.
|
|
176
179
|
|
|
180
|
+
A just-started agent is not promptable until its launch settles. Instead of polling `prompt` and catching
|
|
181
|
+
`agent-not-ready`, wait once: `await kit.waitPromptable(agent, { timeoutMs: 30_000 })` resolves the moment the
|
|
182
|
+
kit's snapshot is promptable — the same gate (`isPromptable`: not `launch_pending` and status idle/working/blocked/done)
|
|
183
|
+
`prompt` itself uses. At the deadline it rejects with code `agent-not-ready`; a pane that closes while waiting rejects
|
|
184
|
+
with code `pane-unavailable`, so the wait never hangs.
|
|
185
|
+
|
|
177
186
|
## Account-specific sign-ins and moves
|
|
178
187
|
|
|
179
188
|
The host supplies account folders and shares conversation history as needed. The kit never reads or copies
|
package/dist/agents.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { HerdrKit } from './kit.ts';
|
|
2
|
-
import type { AgentInstallProbe, AgentInstallState, AgentLaunchFailureReason, AgentStartEvent, HerdrSnapshot } from './types.ts';
|
|
2
|
+
import type { AgentInstallProbe, AgentInstallState, AgentLaunchFailureReason, AgentStartEvent, HerdrSnapshot, HerdrSnapshotAgent } from './types.ts';
|
|
3
3
|
export type Call = (method: string, params: Record<string, unknown>, timeoutMs?: number) => Promise<unknown>;
|
|
4
|
+
export declare function isPromptable(agent: HerdrSnapshotAgent | undefined): boolean;
|
|
4
5
|
export declare function createAgents(ctx: {
|
|
5
6
|
call: Call;
|
|
6
7
|
snapshot(): HerdrSnapshot;
|
|
7
8
|
reread(paneId: string): Promise<void>;
|
|
8
9
|
emitStart?(e: AgentStartEvent): void;
|
|
9
10
|
launchEnv?(): Record<string, string> | undefined;
|
|
10
|
-
}): Pick<HerdrKit, 'startAgent' | 'prompt' | 'sendKeys' | 'wait' | 'read' | 'agentKinds' | 'installedAgentKinds' | 'agentStatus'>;
|
|
11
|
+
}): Pick<HerdrKit, 'startAgent' | 'prompt' | 'waitPromptable' | 'sendKeys' | 'wait' | 'read' | 'agentKinds' | 'installedAgentKinds' | 'agentStatus'>;
|
|
11
12
|
export declare function resolveAgentBinary(kind: string, o: {
|
|
12
13
|
path: string[];
|
|
13
14
|
aliases?: Record<string, string[]>;
|
package/dist/agents.js
CHANGED
|
@@ -12,6 +12,7 @@ import { prepareLaunchEnv } from "./launch-env.js";
|
|
|
12
12
|
import { museNative, MUSE_INSTALL_URL } from "./muse.js";
|
|
13
13
|
const PROMPTABLE = ['idle', 'working', 'blocked', 'done'];
|
|
14
14
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
15
|
+
const WAIT_PROMPTABLE_POLL_MS = 50;
|
|
15
16
|
// `agent.start` lands while the fresh pane is still at its shell prompt: the server answers
|
|
16
17
|
// `agent_pane_busy`/`agent_pane_unavailable` until the pane is ready. The kit retries those for a
|
|
17
18
|
// bounded 5 s, then rolls the pane it created back with `pane.close` like any other start failure.
|
|
@@ -36,6 +37,12 @@ function agentOf(snapshot, paneId) {
|
|
|
36
37
|
}
|
|
37
38
|
return undefined;
|
|
38
39
|
}
|
|
40
|
+
// The one readiness gate (6.4): an agent record is promptable when it exists, is not mid-launch and
|
|
41
|
+
// its status is one Herdr accepts a prompt in. `prompt` refuses with `agent-not-ready` and
|
|
42
|
+
// `waitPromptable` waits, both through this single rule.
|
|
43
|
+
export function isPromptable(agent) {
|
|
44
|
+
return agent !== undefined && agent.launchPending !== true && PROMPTABLE.includes(agent.status);
|
|
45
|
+
}
|
|
39
46
|
// `agent.start` requires a name matching /^[a-z][a-z0-9_-]{0,31}$/; derive one from the kind when absent.
|
|
40
47
|
function agentName(o) {
|
|
41
48
|
const slug = (o.name ?? o.kind).toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^[-_0-9]+/, '').slice(0, 32);
|
|
@@ -205,10 +212,7 @@ export function createAgents(ctx) {
|
|
|
205
212
|
// rides reads only, so a snapshot taken mid-launch goes stale. `interactive_ready` is not gated:
|
|
206
213
|
// Herdr sets it only for agents its own `agent.start` settled, and its `agent.prompt` accepts the
|
|
207
214
|
// rest. A refusal never sends `agent.prompt`.
|
|
208
|
-
const ready = () =>
|
|
209
|
-
const agent = agentOf(ctx.snapshot(), target.paneId);
|
|
210
|
-
return agent !== undefined && agent.launchPending !== true && PROMPTABLE.includes(agent.status);
|
|
211
|
-
};
|
|
215
|
+
const ready = () => isPromptable(agentOf(ctx.snapshot(), target.paneId));
|
|
212
216
|
if (!ready()) {
|
|
213
217
|
await ctx.reread(target.paneId);
|
|
214
218
|
if (!ready())
|
|
@@ -249,6 +253,35 @@ export function createAgents(ctx) {
|
|
|
249
253
|
return { paneId: a.pane_id, terminalId: a.terminal_id, revision: a.revision, status: a.agent_status,
|
|
250
254
|
...(agentSession === undefined ? {} : { agentSession }) };
|
|
251
255
|
}
|
|
256
|
+
// Wait until a just-started agent can take a prompt, instead of the host polling `prompt` and
|
|
257
|
+
// catching `agent-not-ready`. Resolves on the kit's own snapshot once `isPromptable` holds; the
|
|
258
|
+
// snapshot is re-read each pass because `launch_pending` rides `agent.get` reads only. A pane that
|
|
259
|
+
// closed while waiting rejects `pane-unavailable` (confirmed by an `agent_not_found` read); the
|
|
260
|
+
// deadline rejects `agent-not-ready`. Either way the wait never hangs.
|
|
261
|
+
async function waitPromptable(target, o) {
|
|
262
|
+
const deadline = Date.now() + o.timeoutMs;
|
|
263
|
+
const agent = () => agentOf(ctx.snapshot(), target.paneId);
|
|
264
|
+
for (;;) {
|
|
265
|
+
if (isPromptable(agent()))
|
|
266
|
+
return;
|
|
267
|
+
if (agent() === undefined) {
|
|
268
|
+
try {
|
|
269
|
+
await call('agent.get', { target: target.paneId });
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
if (codeOf(error) === 'agent_not_found')
|
|
273
|
+
throw fail('pane-unavailable', 'That pane is no longer available.');
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (Date.now() >= deadline)
|
|
278
|
+
throw fail('agent-not-ready', 'That agent is not ready for a prompt yet.');
|
|
279
|
+
await ctx.reread(target.paneId);
|
|
280
|
+
if (isPromptable(agent()))
|
|
281
|
+
return;
|
|
282
|
+
await sleep(WAIT_PROMPTABLE_POLL_MS);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
252
285
|
async function wait(target, o) {
|
|
253
286
|
const made = await call('agent.wait', { target: target.paneId,
|
|
254
287
|
...(o.until === undefined ? {} : { until: o.until }), timeout_ms: o.timeoutMs });
|
|
@@ -312,7 +345,7 @@ export function createAgents(ctx) {
|
|
|
312
345
|
...(signedIn === 'yes' ? {} : { signInHint: SIGNIN_HINTS[kind] ?? `Sign in to ${kind} on this computer, then check again.` }) };
|
|
313
346
|
}));
|
|
314
347
|
}
|
|
315
|
-
return { startAgent, prompt, sendKeys, wait, read, agentKinds, installedAgentKinds, agentStatus };
|
|
348
|
+
return { startAgent, prompt, waitPromptable, sendKeys, wait, read, agentKinds, installedAgentKinds, agentStatus };
|
|
316
349
|
}
|
|
317
350
|
// One source of truth for install detection (B5's probe plus the shim layer below): the first
|
|
318
351
|
// executable file named for the kind across the probe path, with its absolute path.
|
package/dist/device.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DeviceLink } from '@byokit/
|
|
1
|
+
import type { DeviceLink } from '@byokit/pair';
|
|
2
2
|
import type { AgentRef, AgentStatus, BlockedAgent, HerdrEventName, HerdrEventOf, HerdrMethod, HerdrParams, HerdrResult, HerdrSnapshot, HerdrState, HerdrSubscription, PromptReceipt, StartAgent } from './types.ts';
|
|
3
3
|
export type { HerdrEventName, HerdrEventOf, HerdrEvents, HerdrMethod, HerdrMethods, HerdrParams, HerdrResult, HerdrSubscription, } from './types.ts';
|
|
4
4
|
/** One `hd.events` frame (7.2); `raw` carries a line that was not JSON. */
|
package/dist/index.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ export { HERDR_VERSION, HERDR_PROTOCOL } from './constants.ts';
|
|
|
4
4
|
export { accountKinds, accountKind, type AccountKind } from './kinds.ts';
|
|
5
5
|
export { HerdrKit } from './kit.ts';
|
|
6
6
|
export { agentWords, stateWords, words, WORDS, type WordKey } from './words.ts';
|
|
7
|
-
export { agentProbePath, extraPathDirs, runStatusCommand, agentInstallState, isAutoInstallShim, resolveAgentBinary, classifyStartFailure } from './agents.ts';
|
|
7
|
+
export { agentProbePath, extraPathDirs, runStatusCommand, agentInstallState, isAutoInstallShim, resolveAgentBinary, classifyStartFailure, isPromptable } from './agents.ts';
|
|
8
8
|
export type { AgentCliSignIn, AgentInstallProbe, AgentInstallState, AgentLaunchFailureReason, AgentReadiness, AgentStartEvent, AgentStatusOptions, AgentStatusRunner, AgentRef, AgentStatus, BlockedAgent, HerdrEvent, HerdrEventName, HerdrEventOf, HerdrKitOptions, HerdrMethod, HerdrMethods, HerdrParams, HerdrProtocolRange, HerdrResult, HerdrSnapshot, HerdrState, HerdrSubscription, HerdrSubscribeStop, HerdrTransport, BusyHandoff, Move, MoveToAccount, MoveToAccountResult, MoveResult, OpenSignInTab, PromptReceipt, StartAgent, TerminalSession, AgentTurnEnd, AgentTurnOptions, AgentTurnFiles, AgentTurnResult, AgentTurnResultPolicy, } from './types.ts';
|
package/dist/index.js
CHANGED
|
@@ -5,4 +5,4 @@ export { HERDR_VERSION, HERDR_PROTOCOL } from "./constants.js";
|
|
|
5
5
|
export { accountKinds, accountKind } from "./kinds.js";
|
|
6
6
|
export { HerdrKit } from "./kit.js";
|
|
7
7
|
export { agentWords, stateWords, words, WORDS } from "./words.js";
|
|
8
|
-
export { agentProbePath, extraPathDirs, runStatusCommand, agentInstallState, isAutoInstallShim, resolveAgentBinary, classifyStartFailure } from "./agents.js";
|
|
8
|
+
export { agentProbePath, extraPathDirs, runStatusCommand, agentInstallState, isAutoInstallShim, resolveAgentBinary, classifyStartFailure, isPromptable } from "./agents.js";
|
package/dist/kit.d.ts
CHANGED
|
@@ -81,6 +81,9 @@ export declare class HerdrKit {
|
|
|
81
81
|
timeoutMs: number;
|
|
82
82
|
};
|
|
83
83
|
}): Promise<PromptReceipt>;
|
|
84
|
+
waitPromptable(target: AgentRef, o: {
|
|
85
|
+
timeoutMs: number;
|
|
86
|
+
}): Promise<void>;
|
|
84
87
|
runTurn<T = unknown>(target: AgentRef, o: AgentTurnOptions<T>): Promise<AgentTurnEnd<T>>;
|
|
85
88
|
onTurnEnd(fn: (end: AgentTurnEnd) => void): () => void;
|
|
86
89
|
sendKeys(target: AgentRef, keys: string[]): Promise<void>;
|
package/dist/kit.js
CHANGED
|
@@ -462,6 +462,7 @@ export class HerdrKit {
|
|
|
462
462
|
return () => { this.startListeners.delete(fn); };
|
|
463
463
|
}
|
|
464
464
|
prompt(target, text, o) { return this.agents.prompt(target, text, o); }
|
|
465
|
+
waitPromptable(target, o) { return this.agents.waitPromptable(target, o); }
|
|
465
466
|
runTurn(target, o) { return this.turns.runTurn(target, o); }
|
|
466
467
|
onTurnEnd(fn) { return this.turns.onTurnEnd(fn); }
|
|
467
468
|
sendKeys(target, keys) { return this.agents.sendKeys(target, keys); }
|
package/dist/link.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type IncomingMessage, type ServerResponse } from 'node:http';
|
|
2
|
-
import { type Grant, type Host, type HostOptions } from '@byokit/
|
|
2
|
+
import { type Grant, type Host, type HostOptions } from '@byokit/pair';
|
|
3
3
|
import { type ServeIngress, type Via } from '@byokit/discover';
|
|
4
4
|
import type { RelayClient } from '@byokit/relay';
|
|
5
5
|
import type { HerdrKit } from './kit.ts';
|
package/dist/link.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Host-side link adapter: the hd.* ops over @byokit/
|
|
1
|
+
// Host-side link adapter: the hd.* ops over @byokit/pair, and the serve() wiring (duplicated per kit by design,
|
|
2
2
|
// D3) — built in H7 (docs/runtime-kits.md 7.1-7.3).
|
|
3
3
|
//
|
|
4
4
|
// Permission shape: `allow` lets every request through and `handle`/`stream` refuse with the `link.notAllowed`
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// generic `view-only`/`not-allowed` words instead).
|
|
7
7
|
import { createServer } from 'node:http';
|
|
8
8
|
import { WebSocketServer } from 'ws';
|
|
9
|
-
import { PublicLinkError, } from '@byokit/
|
|
9
|
+
import { PublicLinkError, } from '@byokit/pair';
|
|
10
10
|
import { reach } from '@byokit/discover';
|
|
11
11
|
import { stateWords, words } from "./words.js";
|
|
12
12
|
import { decodeB64Url, sealNotice } from "./notices.js";
|
package/dist/testing/contract.js
CHANGED
|
@@ -390,6 +390,36 @@ export function herdrContract(make, options) {
|
|
|
390
390
|
await kit.closeWorkspace(w2);
|
|
391
391
|
});
|
|
392
392
|
});
|
|
393
|
+
test('contract: waitPromptable waits through a launch-pending agent and resolves', async () => {
|
|
394
|
+
await bench(async ({ kit, fake }) => {
|
|
395
|
+
if (!fake)
|
|
396
|
+
return;
|
|
397
|
+
const paneId = 'w1:p2';
|
|
398
|
+
const agent = fake.world.agents.find((row) => row.pane_id === paneId);
|
|
399
|
+
assert.ok(agent, 'the fake seeds w1:p2');
|
|
400
|
+
agent.launch_pending = true;
|
|
401
|
+
await kit.start();
|
|
402
|
+
// A still-pending launch is not promptable: the deadline rejects agent-not-ready.
|
|
403
|
+
await assert.rejects(kit.waitPromptable({ paneId }, { timeoutMs: 120 }), (e) => e.code === 'agent-not-ready', 'a launch that stays pending rejects with agent-not-ready');
|
|
404
|
+
// Flip the fake ready: the next read clears launch_pending and the wait resolves.
|
|
405
|
+
const waiting = kit.waitPromptable({ paneId }, { timeoutMs: 2000 });
|
|
406
|
+
agent.launch_pending = false;
|
|
407
|
+
await waiting;
|
|
408
|
+
assert.equal(agentStatusOf(kit.snapshot(), paneId).status, 'idle');
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
test('contract: waitPromptable rejects pane-unavailable when the pane closes mid-wait', async () => {
|
|
412
|
+
await bench(async ({ kit, fake }) => {
|
|
413
|
+
if (!fake)
|
|
414
|
+
return;
|
|
415
|
+
const paneId = 'w1:p2';
|
|
416
|
+
fake.world.agents.find((row) => row.pane_id === paneId).launch_pending = true;
|
|
417
|
+
await kit.start();
|
|
418
|
+
const waiting = kit.waitPromptable({ paneId }, { timeoutMs: 2000 });
|
|
419
|
+
await kit.closePane(paneId);
|
|
420
|
+
await assert.rejects(waiting, (e) => e.code === 'pane-unavailable', 'a pane closed while waiting rejects pane-unavailable instead of hanging');
|
|
421
|
+
});
|
|
422
|
+
});
|
|
393
423
|
test('contract: the cli answers --version and the terminal echoes', async () => {
|
|
394
424
|
await bench(async ({ kit }) => {
|
|
395
425
|
await kit.start();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byokit/herdr",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"description": "Drive the Herdr on this computer — workspaces, tabs, panes, agents and blocked-approval answers — from an app, or hand it to a phone over @byokit/
|
|
3
|
+
"version": "0.7.3",
|
|
4
|
+
"description": "Drive the Herdr on this computer — workspaces, tabs, panes, agents and blocked-approval answers — from an app, or hand it to a phone over @byokit/pair.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"prepack": "tsc -b && node ../../scripts/fix-words-dts.cjs"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@byokit/
|
|
56
|
-
"@byokit/relay": "0.
|
|
57
|
-
"@byokit/discover": "0.
|
|
55
|
+
"@byokit/pair": "0.9.0",
|
|
56
|
+
"@byokit/relay": "0.6.0",
|
|
57
|
+
"@byokit/discover": "0.7.0",
|
|
58
58
|
"@byokit/seal": "0.3.0",
|
|
59
|
-
"ws": "8.
|
|
59
|
+
"ws": "8.22.0"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|