@byokit/herdr 0.7.2 → 0.8.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 +39 -0
- package/README.md +29 -13
- package/dist/accounts.d.ts +2 -1
- package/dist/accounts.js +34 -29
- package/dist/agents.d.ts +3 -2
- package/dist/agents.js +98 -30
- package/dist/close.js +15 -6
- package/dist/constants.js +1 -1
- package/dist/device.d.ts +19 -1
- package/dist/device.js +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/kit.d.ts +3 -0
- package/dist/kit.js +3 -1
- package/dist/link.d.ts +1 -1
- package/dist/link.js +139 -5
- package/dist/muse.d.ts +2 -0
- package/dist/muse.js +7 -1
- package/dist/notices.js +10 -16
- package/dist/testing/contract.js +138 -4
- package/dist/testing/fake-herdr/server.js +61 -1
- package/dist/turn-files.d.ts +1 -1
- package/dist/turn-files.js +6 -1
- package/dist/turns.js +7 -5
- package/dist/types.d.ts +8 -2
- package/dist/words.json +1 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.8.0 (2026-10-12)
|
|
6
|
+
|
|
7
|
+
- Dependency update: pins @byokit/discover 0.8.0.
|
|
8
|
+
- Dependency update: pins @byokit/relay 0.6.1.
|
|
9
|
+
- Dependency update: pins @byokit/pair 0.10.0.
|
|
10
|
+
|
|
11
|
+
- FIX: A Node script (or any non-shell interpreter) whose path contains `mise` is now a real installed binary, so `agentStatus` runs its sign-in check instead of skipping it as an auto-install shim; only a shell launcher that mentions mise still reads `installs-on-first-start`.
|
|
12
|
+
- `startAgent` accepts a workspace label as `place.workspace`: the agent lands in the one workspace with that label (new tab, `created: false`) or in a workspace created with that label (`created: true`). Two workspaces with the label refuse with `workspace-ambiguous` and create nothing, and a failed start rolls back only what the kit made (the fresh pane, or the fresh workspace), never a reused workspace. `AgentRef` carries the new `created` flag.
|
|
13
|
+
- New control link ops `hd.rename { pane | tab | workspace, label }`, `hd.focus { pane | tab | workspace }` and
|
|
14
|
+
`hd.layout { root, focus?, tabId?, tabLabel?, workspaceId? }`, with device `rename()`, `focus()` and
|
|
15
|
+
`layout()`. Each copies `hd.close`'s scope rule: the one pane, tab (through its owning workspace) or
|
|
16
|
+
workspace it names must be in `scopeOf(grant)`, or the device hears the `link.notAllowed` sentence and no
|
|
17
|
+
Herdr call is made; a `view` grant is refused. They map only to the typed `pane.rename`/`tab.rename`/
|
|
18
|
+
`workspace.rename`, `pane.focus`/`tab.focus`/`workspace.focus` and `layout.apply` calls, never through
|
|
19
|
+
`hd.call`. The fake Herdr now answers `layout.apply` and emits `layout.updated`.
|
|
20
|
+
- `sealNotice`/`openNotice` (and the portable `./device` re-export) now delegate to `@byokit/seal`'s generic
|
|
21
|
+
`sealNotice`/`openNotice` instead of sealing and opening the box here: the device seed becomes the box secret
|
|
22
|
+
through `boxKeyPairFromSeed`, and the opened value is still shape-checked as a `BlockedAgent`. The wire format is
|
|
23
|
+
unchanged, so a notice sealed by an earlier kit still opens, and `seal.openNotice` opens a herdr notice. The
|
|
24
|
+
hand-rolled base64url helpers stay only for the box-key registration text (`boxPublicKeyB64`, link.ts's
|
|
25
|
+
`decodeB64Url`), because seal exports no base64url.
|
|
26
|
+
- FIX: A refused widening close now tells the app which scope it would widen to: `closePane`/`closeTab`/`closeWorkspace` still throw `pane-close-would-widen`/`tab-close-would-widen`/`workspace-close-would-widen`, but each also carries the wider target as `{ scope: 'tab' | 'workspace', id, label }` (a pane's tab, a tab's workspace, a worktree group named by its root), so the UI can offer the bigger close instead. `hd.close` over a link maps all three codes to a `PublicLinkError` with the `close.wouldWiden` words, so a device hears plain words instead of a bare failure.
|
|
27
|
+
- FIX: `moveToAccount` refuses signed-out managed targets before waiting or creating a pane, preserving the source; generic `move` remains caller-gated and unknown readiness still proceeds.
|
|
28
|
+
- FIX: Muse readiness routes its mise check through the shared shell-launcher predicate, so a non-shell binary whose interpreter path contains `mise` reads installed instead of launcher-only; genuine mise shell shims still read launcher-only.
|
|
29
|
+
- FIX: `runTurn` accepts an agent that is already `done` when the turn starts, treating it like `idle` at turn start; previously the call was refused with `turn-not-idle`/`turn-busy`.
|
|
30
|
+
- FIX: `runTurn` accepts `files: false` to skip the file scan entirely (`changedFiles` is then always empty), so a turn rooted in a folder with more than 10000 entries no longer fails with `turn: file limit`.
|
|
31
|
+
|
|
32
|
+
## 0.7.3 (2026-10-11)
|
|
33
|
+
|
|
34
|
+
- Dependency update: pins @byokit/discover 0.7.0.
|
|
35
|
+
|
|
36
|
+
- `@byokit/herdr` exports `isPromptable(agent)`, the single readiness gate `prompt` already used, and adds
|
|
37
|
+
`kit.waitPromptable(target, { timeoutMs })`: it waits until a just-started agent can take a prompt instead of the
|
|
38
|
+
host polling `prompt` and catching `agent-not-ready`. It resolves once the kit's snapshot is promptable, rejects
|
|
39
|
+
`agent-not-ready` at the deadline, and rejects `pane-unavailable` when the pane closes mid-wait.
|
|
40
|
+
- The README's rename note and examples now point at `@byokit/pair` (the phone hand-off host) and the
|
|
41
|
+
example screenshots were re-captured from today's herdr-kit phone page. Docs only; no typed surface changed.
|
|
42
|
+
- Dependency update: depend on `@byokit/pair` 0.9.0 instead of its deprecated `@byokit/link` shim.
|
|
43
|
+
|
|
5
44
|
## 0.7.2 (2026-10-09)
|
|
6
45
|
|
|
7
46
|
- Dependency update: pins @byokit/relay 0.6.0.
|
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
|
|
@@ -196,6 +205,7 @@ if (moved.ok) console.log(moved.session); // new pane to follow
|
|
|
196
205
|
|
|
197
206
|
A move accepts mapped managed kinds, including Claude (`CLAUDE_CONFIG_DIR`), Codex (`CODEX_HOME`) and Pi
|
|
198
207
|
(`PI_CODING_AGENT_DIR`, `--session <id|absolute path>`). Hosts own cross-account history sharing.
|
|
208
|
+
`moveToAccount` checks the target managed folder once with `agentStatus`, after immediate refusals and before waiting or splitting. A signed-out target returns `signed_out` and “Sign in to {name} first.” (`name` defaults to “this account”), with `live` naming the unchanged source and no new pane. Unknown readiness proceeds (including Pi); generic `move` has no check, so Accounts hosts call `cliAccounts.launch` and pass its `set`/`unset` only on success. The check is not a lock: a later sign-out still starts only in the selected folder, with existing `start_failed` rollback and no default-login retry.
|
|
199
209
|
It verifies the new shell's effective
|
|
200
210
|
account folder, resumes the conversation there, waits for a ready session, then closes the old pane. A failed
|
|
201
211
|
start preserves the original. A failed source close rolls back the new pane only after fresh reads verify
|
|
@@ -277,12 +287,17 @@ the extra install dirs a service PATH omits (`extraPathDirs`: `~/.local/bin`, th
|
|
|
277
287
|
Sign-in comes only from each CLI's own documented non-secret status command, with a 10 s timeout;
|
|
278
288
|
a kind whose CLI has none, or whose command gives no answer, reads `unknown`. Credential files
|
|
279
289
|
are never opened, read or statted — only the signed-in boolean is kept. Install detection tells a
|
|
280
|
-
real runnable binary apart from an auto-install launcher: a
|
|
281
|
-
on PATH at all, retains the
|
|
290
|
+
real runnable binary apart from an auto-install launcher: a shell script (a `sh`, `bash`, `zsh`,
|
|
291
|
+
`dash`, `ksh` or `ash` shebang) whose text mentions mise, or nothing on PATH at all, retains the
|
|
292
|
+
legacy `installState: 'installs-on-first-start'` with `installed: false`. A binary whose interpreter
|
|
293
|
+
merely lives under mise (for example a Node script) reads `installed`.
|
|
282
294
|
**Muse is different:** no executable, an auto-install shim, or an official launcher without its selected native release,
|
|
283
295
|
reads `installState: 'missing'`. Stock Herdr does not install an absent Muse command. That covers `pi`, which
|
|
284
296
|
Herdr installs via mise: a missing or shimmed `pi` reads `installed: false` with `installs on
|
|
285
|
-
first start`, while a real `pi` binary reads `installed: true`.
|
|
297
|
+
first start`, while a real `pi` binary reads `installed: true`. The `museReadiness()` probe's
|
|
298
|
+
`launcher-only` state is a separate check, not this shell-shebang rule: it flags any `#!` file whose
|
|
299
|
+
head contains `mise`, so a Node script under mise reads `launcher-only` there but `installed` on the
|
|
300
|
+
agent path.
|
|
286
301
|
|
|
287
302
|
```ts
|
|
288
303
|
import { HerdrKit } from '@byokit/herdr';
|
|
@@ -447,6 +462,7 @@ targets, do not traverse symlinks, always exclude `.git`, and ignore special fil
|
|
|
447
462
|
and restored during the turn do not appear. Use `files.exclude` to prune generated or sensitive
|
|
448
463
|
folders. Each scan defaults to 10000 entries and 128 MiB of regular-file content; set
|
|
449
464
|
`files.maxFiles`/`maxBytes` to change these limits. An incomplete scan rejects the call.
|
|
465
|
+
Pass `files: false` to skip the scan entirely (`changedFiles` is then always empty).
|
|
450
466
|
|
|
451
467
|
Reserve the pane and folder exclusively while the turn runs. Overlapping calls in one kit are
|
|
452
468
|
refused, but changes by other apps or processes cannot be attributed to this agent. The supplied
|
package/dist/accounts.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Call } from './agents.ts';
|
|
2
|
-
import type { AgentRef, Move, MoveResult, MoveToAccount, MoveToAccountResult, OpenSignInTab, StartAgent } from './types.ts';
|
|
2
|
+
import type { AgentReadiness, AgentStatusOptions, AgentRef, Move, MoveResult, MoveToAccount, MoveToAccountResult, OpenSignInTab, StartAgent } from './types.ts';
|
|
3
3
|
export declare function createAccountPanes(ctx: {
|
|
4
4
|
call: Call;
|
|
5
5
|
startAgent(o: StartAgent): Promise<AgentRef>;
|
|
6
|
+
agentStatus(kinds: readonly string[], o?: AgentStatusOptions): Promise<AgentReadiness[]>;
|
|
6
7
|
}): {
|
|
7
8
|
openSignInTab: (o: OpenSignInTab) => Promise<AgentRef>;
|
|
8
9
|
move: (o: Move) => Promise<MoveResult>;
|
package/dist/accounts.js
CHANGED
|
@@ -78,7 +78,7 @@ export function createAccountPanes(ctx) {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
async function perform(target, o, request) {
|
|
81
|
-
const failed = (code, live = target.paneId) => ({ ok: false, code, message: words(`move.${code}
|
|
81
|
+
const failed = (code, live = target.paneId) => ({ ok: false, code, message: words(`move.${code}`, { name: o.name ?? 'this account' }), ...(live === null ? {} : { live }) });
|
|
82
82
|
if (moving.has(target.paneId))
|
|
83
83
|
return failed('busy');
|
|
84
84
|
moving.add(target.paneId);
|
|
@@ -114,34 +114,6 @@ export function createAccountPanes(ctx) {
|
|
|
114
114
|
if (!Number.isFinite(handoff.waitMs) || handoff.waitMs <= 0 || handoff.waitMs > 300_000
|
|
115
115
|
|| !Number.isSafeInteger(agent.state_change_seq) || agent.state_change_seq < 0)
|
|
116
116
|
return failed('unsupported');
|
|
117
|
-
if (agent.agent_status === 'working') {
|
|
118
|
-
try {
|
|
119
|
-
await call('agent.wait', { target: target.paneId, until: ['idle', 'done', 'blocked'],
|
|
120
|
-
timeout_ms: handoff.waitMs }, handoff.waitMs + 5000);
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
return failed('busy');
|
|
124
|
-
}
|
|
125
|
-
let current;
|
|
126
|
-
try {
|
|
127
|
-
current = (await call('agent.get', { target: target.paneId }))?.agent;
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
return failed('changed', null);
|
|
131
|
-
}
|
|
132
|
-
if (!current || current.launch_pending === true || current.agent !== agent.agent
|
|
133
|
-
|| current.terminal_id !== agent.terminal_id || current.agent_session?.agent !== session.agent
|
|
134
|
-
|| current.agent_session?.kind !== session.kind || current.agent_session?.value !== session.value)
|
|
135
|
-
return failed('changed', null);
|
|
136
|
-
if (current.agent_status === 'blocked')
|
|
137
|
-
return failed('blocked');
|
|
138
|
-
if (!['idle', 'done'].includes(current.agent_status))
|
|
139
|
-
return failed('busy');
|
|
140
|
-
if (!Number.isSafeInteger(current.state_change_seq) || current.state_change_seq < 0)
|
|
141
|
-
return failed('unsupported');
|
|
142
|
-
agent = current;
|
|
143
|
-
session = current.agent_session;
|
|
144
|
-
}
|
|
145
117
|
}
|
|
146
118
|
const kind = agent.agent;
|
|
147
119
|
const metadata = accountKind(o.provider);
|
|
@@ -161,6 +133,39 @@ export function createAccountPanes(ctx) {
|
|
|
161
133
|
if (!Number.isFinite(timeout) || timeout <= 0)
|
|
162
134
|
return failed('start_failed');
|
|
163
135
|
const variable = metadata.folderVar;
|
|
136
|
+
if (request === undefined) {
|
|
137
|
+
const [status] = await ctx.agentStatus([kind], { folders: { [kind]: o.folder }, env: o.env });
|
|
138
|
+
if (status?.signedIn === 'no')
|
|
139
|
+
return failed('signed_out');
|
|
140
|
+
}
|
|
141
|
+
if (handoff?.busy === 'wait' && agent.agent_status === 'working') {
|
|
142
|
+
try {
|
|
143
|
+
await call('agent.wait', { target: target.paneId, until: ['idle', 'done', 'blocked'],
|
|
144
|
+
timeout_ms: handoff.waitMs }, handoff.waitMs + 5000);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return failed('busy');
|
|
148
|
+
}
|
|
149
|
+
let current;
|
|
150
|
+
try {
|
|
151
|
+
current = (await call('agent.get', { target: target.paneId }))?.agent;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return failed('changed', null);
|
|
155
|
+
}
|
|
156
|
+
if (!current || current.launch_pending === true || current.agent !== agent.agent
|
|
157
|
+
|| current.terminal_id !== agent.terminal_id || current.agent_session?.agent !== session.agent
|
|
158
|
+
|| current.agent_session?.kind !== session.kind || current.agent_session?.value !== session.value)
|
|
159
|
+
return failed('changed', null);
|
|
160
|
+
if (current.agent_status === 'blocked')
|
|
161
|
+
return failed('blocked');
|
|
162
|
+
if (!['idle', 'done'].includes(current.agent_status))
|
|
163
|
+
return failed('busy');
|
|
164
|
+
if (!Number.isSafeInteger(current.state_change_seq) || current.state_change_seq < 0)
|
|
165
|
+
return failed('unsupported');
|
|
166
|
+
agent = current;
|
|
167
|
+
session = current.agent_session;
|
|
168
|
+
}
|
|
164
169
|
let paneId;
|
|
165
170
|
let replacement;
|
|
166
171
|
try {
|
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
|
@@ -9,9 +9,10 @@ import { delimiter, isAbsolute, join } from 'node:path';
|
|
|
9
9
|
import { words } from "./words.js";
|
|
10
10
|
import { accountKind } from "./kinds.js";
|
|
11
11
|
import { prepareLaunchEnv } from "./launch-env.js";
|
|
12
|
-
import { museNative, MUSE_INSTALL_URL } from "./muse.js";
|
|
12
|
+
import { museNative, MUSE_INSTALL_URL, isMiseLauncher } 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);
|
|
@@ -55,7 +62,9 @@ export function createAgents(ctx) {
|
|
|
55
62
|
};
|
|
56
63
|
// Every placement takes its pane id from the server's answer, never a prediction (6.4).
|
|
57
64
|
// Placements that make a pane (every path but `pane`) roll it back with `pane.close` when the
|
|
58
|
-
// start fails, so a failed start leaves no extra pane behind.
|
|
65
|
+
// start fails, so a failed start leaves no extra pane behind. A label placement that created
|
|
66
|
+
// its workspace rolls the workspace back with `workspace.close` instead, so no empty workspace
|
|
67
|
+
// survives either; a reused workspace is never closed.
|
|
59
68
|
async function startAgent(o) {
|
|
60
69
|
const emit = (e) => {
|
|
61
70
|
try {
|
|
@@ -123,7 +132,24 @@ export function createAgents(ctx) {
|
|
|
123
132
|
XDG_STATE_HOME: join(home, '.local', 'state'), XDG_CACHE_HOME: join(home, '.cache'), ...o.env
|
|
124
133
|
} };
|
|
125
134
|
let paneId;
|
|
126
|
-
let
|
|
135
|
+
let createdPane = false; // a pane this start made: roll it back on failure
|
|
136
|
+
let createdWorkspace; // a workspace this start made: roll it back instead
|
|
137
|
+
let workspaceCreated = false; // reported as `created`: a workspace was made
|
|
138
|
+
async function rollback() {
|
|
139
|
+
if (createdWorkspace !== undefined) {
|
|
140
|
+
try {
|
|
141
|
+
await call('workspace.close', { workspace_id: createdWorkspace });
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
catch { /* fall through to the pane when the workspace close misses */ }
|
|
145
|
+
}
|
|
146
|
+
if (createdPane && paneId !== undefined) {
|
|
147
|
+
try {
|
|
148
|
+
await call('pane.close', { pane_id: paneId });
|
|
149
|
+
}
|
|
150
|
+
catch { /* rollback is best-effort */ }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
127
153
|
if (o.worktree !== undefined) {
|
|
128
154
|
const made = await call('worktree.create', { cwd: o.cwd, focus: false, ...env,
|
|
129
155
|
...(o.worktree.branch === undefined ? {} : { branch: o.worktree.branch }),
|
|
@@ -136,23 +162,50 @@ export function createAgents(ctx) {
|
|
|
136
162
|
paneId = rootPaneOf(made) ?? rootPaneOf(await call('tab.create', {
|
|
137
163
|
workspace_id: made?.workspace?.workspace_id, cwd: checkout, focus: false, ...env,
|
|
138
164
|
}));
|
|
139
|
-
|
|
165
|
+
createdPane = true;
|
|
166
|
+
workspaceCreated = true;
|
|
167
|
+
}
|
|
168
|
+
else if ('workspace' in o.place && o.place.workspace !== 'new') {
|
|
169
|
+
// A workspace label (6.4): reuse the one workspace with this label, or create it.
|
|
170
|
+
// The lookup is a fresh `workspace.list`, never the kit's tree, so a workspace made
|
|
171
|
+
// moments ago is found. Two matches refuse with `workspace-ambiguous` and pick nothing.
|
|
172
|
+
const label = o.place.workspace;
|
|
173
|
+
const listed = await call('workspace.list', {});
|
|
174
|
+
const workspaces = Array.isArray(listed?.workspaces) ? listed.workspaces : [];
|
|
175
|
+
const matches = workspaces.filter((w) => isObj(w) && w.label === label);
|
|
176
|
+
if (matches.length > 1)
|
|
177
|
+
throw fail('workspace-ambiguous', `More than one workspace is named "${label}". Rename one, then try again.`);
|
|
178
|
+
if (matches.length === 1) {
|
|
179
|
+
paneId = rootPaneOf(await call('tab.create', { workspace_id: matches[0].workspace_id, cwd: o.cwd,
|
|
180
|
+
...(o.place.label === undefined ? {} : { label: o.place.label }), focus: false, ...env }));
|
|
181
|
+
createdPane = true;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
const made = await call('workspace.create', { cwd: o.cwd, label, focus: false, ...env });
|
|
185
|
+
paneId = rootPaneOf(made);
|
|
186
|
+
createdPane = true;
|
|
187
|
+
workspaceCreated = true;
|
|
188
|
+
if (isObj(made?.workspace) && typeof made.workspace.workspace_id === 'string') {
|
|
189
|
+
createdWorkspace = made.workspace.workspace_id;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
140
192
|
}
|
|
141
193
|
else if ('workspace' in o.place) {
|
|
142
194
|
paneId = rootPaneOf(await call('workspace.create', { cwd: o.cwd,
|
|
143
195
|
...(o.place.label === undefined ? {} : { label: o.place.label }), focus: false, ...env }));
|
|
144
|
-
|
|
196
|
+
createdPane = true;
|
|
197
|
+
workspaceCreated = true;
|
|
145
198
|
}
|
|
146
199
|
else if ('tab' in o.place) {
|
|
147
200
|
paneId = rootPaneOf(await call('tab.create', { workspace_id: o.place.workspaceId, cwd: o.cwd,
|
|
148
201
|
...(o.place.label === undefined ? {} : { label: o.place.label }), focus: false, ...env }));
|
|
149
|
-
|
|
202
|
+
createdPane = true;
|
|
150
203
|
}
|
|
151
204
|
else if ('split' in o.place) {
|
|
152
205
|
const made = await call('pane.split', { target_pane_id: o.place.split,
|
|
153
206
|
direction: o.place.direction, focus: false, ...env });
|
|
154
207
|
paneId = typeof made?.pane?.pane_id === 'string' ? made.pane.pane_id : undefined;
|
|
155
|
-
|
|
208
|
+
createdPane = true;
|
|
156
209
|
}
|
|
157
210
|
else {
|
|
158
211
|
paneId = o.place.pane;
|
|
@@ -164,12 +217,7 @@ export function createAgents(ctx) {
|
|
|
164
217
|
await prepareLaunchEnv(call, paneId, launch, timeout);
|
|
165
218
|
}
|
|
166
219
|
catch (error) {
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
await call('pane.close', { pane_id: paneId });
|
|
170
|
-
}
|
|
171
|
-
catch { /* best effort */ }
|
|
172
|
-
}
|
|
220
|
+
await rollback();
|
|
173
221
|
throw error;
|
|
174
222
|
}
|
|
175
223
|
}
|
|
@@ -181,19 +229,14 @@ export function createAgents(ctx) {
|
|
|
181
229
|
for (;;) {
|
|
182
230
|
try {
|
|
183
231
|
await call('agent.start', params, timeout + 5000);
|
|
184
|
-
return { paneId, name };
|
|
232
|
+
return { paneId, name, created: workspaceCreated };
|
|
185
233
|
}
|
|
186
234
|
catch (error) {
|
|
187
235
|
if (START_RETRYABLE.has(codeOf(error) ?? '') && Date.now() < deadline) {
|
|
188
236
|
await sleep(START_RETRY_DELAY_MS);
|
|
189
237
|
continue;
|
|
190
238
|
}
|
|
191
|
-
|
|
192
|
-
try {
|
|
193
|
-
await call('pane.close', { pane_id: paneId });
|
|
194
|
-
}
|
|
195
|
-
catch { /* rollback is best-effort */ }
|
|
196
|
-
}
|
|
239
|
+
await rollback();
|
|
197
240
|
if (launch)
|
|
198
241
|
throw fail('start_failed', words('agent.notReady'));
|
|
199
242
|
throw error;
|
|
@@ -205,10 +248,7 @@ export function createAgents(ctx) {
|
|
|
205
248
|
// rides reads only, so a snapshot taken mid-launch goes stale. `interactive_ready` is not gated:
|
|
206
249
|
// Herdr sets it only for agents its own `agent.start` settled, and its `agent.prompt` accepts the
|
|
207
250
|
// 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
|
-
};
|
|
251
|
+
const ready = () => isPromptable(agentOf(ctx.snapshot(), target.paneId));
|
|
212
252
|
if (!ready()) {
|
|
213
253
|
await ctx.reread(target.paneId);
|
|
214
254
|
if (!ready())
|
|
@@ -249,6 +289,35 @@ export function createAgents(ctx) {
|
|
|
249
289
|
return { paneId: a.pane_id, terminalId: a.terminal_id, revision: a.revision, status: a.agent_status,
|
|
250
290
|
...(agentSession === undefined ? {} : { agentSession }) };
|
|
251
291
|
}
|
|
292
|
+
// Wait until a just-started agent can take a prompt, instead of the host polling `prompt` and
|
|
293
|
+
// catching `agent-not-ready`. Resolves on the kit's own snapshot once `isPromptable` holds; the
|
|
294
|
+
// snapshot is re-read each pass because `launch_pending` rides `agent.get` reads only. A pane that
|
|
295
|
+
// closed while waiting rejects `pane-unavailable` (confirmed by an `agent_not_found` read); the
|
|
296
|
+
// deadline rejects `agent-not-ready`. Either way the wait never hangs.
|
|
297
|
+
async function waitPromptable(target, o) {
|
|
298
|
+
const deadline = Date.now() + o.timeoutMs;
|
|
299
|
+
const agent = () => agentOf(ctx.snapshot(), target.paneId);
|
|
300
|
+
for (;;) {
|
|
301
|
+
if (isPromptable(agent()))
|
|
302
|
+
return;
|
|
303
|
+
if (agent() === undefined) {
|
|
304
|
+
try {
|
|
305
|
+
await call('agent.get', { target: target.paneId });
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
if (codeOf(error) === 'agent_not_found')
|
|
309
|
+
throw fail('pane-unavailable', 'That pane is no longer available.');
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (Date.now() >= deadline)
|
|
314
|
+
throw fail('agent-not-ready', 'That agent is not ready for a prompt yet.');
|
|
315
|
+
await ctx.reread(target.paneId);
|
|
316
|
+
if (isPromptable(agent()))
|
|
317
|
+
return;
|
|
318
|
+
await sleep(WAIT_PROMPTABLE_POLL_MS);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
252
321
|
async function wait(target, o) {
|
|
253
322
|
const made = await call('agent.wait', { target: target.paneId,
|
|
254
323
|
...(o.until === undefined ? {} : { until: o.until }), timeout_ms: o.timeoutMs });
|
|
@@ -312,7 +381,7 @@ export function createAgents(ctx) {
|
|
|
312
381
|
...(signedIn === 'yes' ? {} : { signInHint: SIGNIN_HINTS[kind] ?? `Sign in to ${kind} on this computer, then check again.` }) };
|
|
313
382
|
}));
|
|
314
383
|
}
|
|
315
|
-
return { startAgent, prompt, sendKeys, wait, read, agentKinds, installedAgentKinds, agentStatus };
|
|
384
|
+
return { startAgent, prompt, waitPromptable, sendKeys, wait, read, agentKinds, installedAgentKinds, agentStatus };
|
|
316
385
|
}
|
|
317
386
|
// One source of truth for install detection (B5's probe plus the shim layer below): the first
|
|
318
387
|
// executable file named for the kind across the probe path, with its absolute path.
|
|
@@ -352,9 +421,8 @@ function readHead(file) {
|
|
|
352
421
|
catch { /* already gone */ }
|
|
353
422
|
}
|
|
354
423
|
}
|
|
355
|
-
// A mise-style auto-install launcher
|
|
356
|
-
//
|
|
357
|
-
// Mach-O) never match: the shebang gate keeps a stray "mise" substring ("promise", …) out.
|
|
424
|
+
// A mise-style auto-install launcher is a shell script that goes through mise — the found
|
|
425
|
+
// "binary" runs, but the first run installs the agent instead (see isMiseLauncher in muse.ts).
|
|
358
426
|
export function isAutoInstallShim(file, readFile = readHead) {
|
|
359
427
|
let head;
|
|
360
428
|
try {
|
|
@@ -363,7 +431,7 @@ export function isAutoInstallShim(file, readFile = readHead) {
|
|
|
363
431
|
catch {
|
|
364
432
|
return false;
|
|
365
433
|
}
|
|
366
|
-
return head !== undefined &&
|
|
434
|
+
return head !== undefined && isMiseLauncher(head);
|
|
367
435
|
}
|
|
368
436
|
// Muse has no absent-CLI auto-installer in stock Herdr. Its official launcher needs the
|
|
369
437
|
// selected native release too. Other kinds retain their legacy shim classification.
|
|
@@ -381,7 +449,7 @@ export function agentInstallState(kind, o) {
|
|
|
381
449
|
return { kind, state: 'missing', path: found.path };
|
|
382
450
|
}
|
|
383
451
|
const head = (o?.readFile ?? readHead)(found.path);
|
|
384
|
-
if (head
|
|
452
|
+
if (head !== undefined && (isMiseLauncher(head) || (head.startsWith('#!') && head.includes('MUSE_CHANNEL') && !museNative(found.path)))) {
|
|
385
453
|
return { kind, state: 'missing', path: found.path };
|
|
386
454
|
}
|
|
387
455
|
return { kind, state: 'installed', path: found.path };
|
package/dist/close.js
CHANGED
|
@@ -4,7 +4,7 @@ const codeOf = (error) => {
|
|
|
4
4
|
return code;
|
|
5
5
|
return /herdr: ([a-z0-9_]+):/i.exec(error instanceof Error ? error.message : String(error))?.[1];
|
|
6
6
|
};
|
|
7
|
-
const fail = (code, message) => Object.assign(new Error(message), { code });
|
|
7
|
+
const fail = (code, message, wider) => Object.assign(new Error(message), { code }, wider ?? {});
|
|
8
8
|
const unavailable = (kind) => fail(`${kind}-unavailable`, `That ${kind} is no longer available.`);
|
|
9
9
|
const count = (value) => (typeof value === 'number' && Number.isFinite(value) ? value : 0);
|
|
10
10
|
export async function closePane(call, paneId) {
|
|
@@ -21,17 +21,20 @@ export async function closePane(call, paneId) {
|
|
|
21
21
|
if (typeof tabId !== 'string')
|
|
22
22
|
throw unavailable('pane');
|
|
23
23
|
let paneCount = 0;
|
|
24
|
+
let tabLabel;
|
|
24
25
|
try {
|
|
25
26
|
const made = (await call('tab.get', { tab_id: tabId }));
|
|
26
27
|
paneCount = count(made?.tab?.pane_count);
|
|
28
|
+
tabLabel = typeof made?.tab?.label === 'string' ? made.tab.label : undefined;
|
|
27
29
|
}
|
|
28
30
|
catch (error) {
|
|
29
31
|
if (codeOf(error) === 'tab_not_found')
|
|
30
32
|
throw unavailable('pane');
|
|
31
33
|
throw error;
|
|
32
34
|
}
|
|
33
|
-
if (paneCount <= 1)
|
|
34
|
-
throw fail('pane-close-would-widen', 'Closing this pane would close its tab; close the tab instead.');
|
|
35
|
+
if (paneCount <= 1) {
|
|
36
|
+
throw fail('pane-close-would-widen', 'Closing this pane would close its tab; close the tab instead.', { scope: 'tab', id: tabId, label: tabLabel ?? '' });
|
|
37
|
+
}
|
|
35
38
|
await call('pane.close', { pane_id: paneId });
|
|
36
39
|
}
|
|
37
40
|
export async function closeTab(call, tabId) {
|
|
@@ -48,24 +51,29 @@ export async function closeTab(call, tabId) {
|
|
|
48
51
|
if (typeof workspaceId !== 'string')
|
|
49
52
|
throw unavailable('tab');
|
|
50
53
|
let tabCount = 0;
|
|
54
|
+
let workspaceLabel;
|
|
51
55
|
try {
|
|
52
56
|
const made = (await call('workspace.get', { workspace_id: workspaceId }));
|
|
53
57
|
tabCount = count(made?.workspace?.tab_count);
|
|
58
|
+
workspaceLabel = typeof made?.workspace?.label === 'string' ? made.workspace.label : undefined;
|
|
54
59
|
}
|
|
55
60
|
catch (error) {
|
|
56
61
|
if (codeOf(error) === 'workspace_not_found')
|
|
57
62
|
throw unavailable('tab');
|
|
58
63
|
throw error;
|
|
59
64
|
}
|
|
60
|
-
if (tabCount <= 1)
|
|
61
|
-
throw fail('tab-close-would-widen', 'Closing this tab would close its workspace; close the workspace instead.');
|
|
65
|
+
if (tabCount <= 1) {
|
|
66
|
+
throw fail('tab-close-would-widen', 'Closing this tab would close its workspace; close the workspace instead.', { scope: 'workspace', id: workspaceId, label: workspaceLabel ?? '' });
|
|
67
|
+
}
|
|
62
68
|
await call('tab.close', { tab_id: tabId });
|
|
63
69
|
}
|
|
64
70
|
export async function closeWorkspace(call, workspaceId) {
|
|
65
71
|
let worktree;
|
|
72
|
+
let workspaceLabel;
|
|
66
73
|
try {
|
|
67
74
|
const made = (await call('workspace.get', { workspace_id: workspaceId }));
|
|
68
75
|
worktree = made?.workspace?.worktree;
|
|
76
|
+
workspaceLabel = typeof made?.workspace?.label === 'string' ? made.workspace.label : undefined;
|
|
69
77
|
}
|
|
70
78
|
catch (error) {
|
|
71
79
|
if (codeOf(error) === 'workspace_not_found')
|
|
@@ -78,7 +86,8 @@ export async function closeWorkspace(call, workspaceId) {
|
|
|
78
86
|
const group = (Array.isArray(made?.workspaces) ? made.workspaces : [])
|
|
79
87
|
.filter((w) => w?.worktree?.repo_key === worktree.repo_key).length;
|
|
80
88
|
if (group >= 2) {
|
|
81
|
-
|
|
89
|
+
// The group is anchored at this root, so the wider target is named by that root workspace.
|
|
90
|
+
throw fail('workspace-close-would-widen', 'Closing this workspace would close its worktree group; close the linked checkouts instead.', { scope: 'workspace', id: workspaceId, label: workspaceLabel ?? '' });
|
|
82
91
|
}
|
|
83
92
|
}
|
|
84
93
|
await call('workspace.close', { workspace_id: workspaceId });
|
package/dist/constants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The pinned Herdr release (docs/runtime-kits.md D5) and the protocol this kit speaks.
|
|
2
2
|
export const HERDR_VERSION = '0.9.1';
|
|
3
3
|
// From the v0.9.1 schema snapshot (schema/herdr-api-0.9.1.json, docs/runtime-kits.md 6.7);
|
|
4
|
-
// regenerated by scripts/gen-types.ts
|
|
4
|
+
// regenerated by scripts/gen-types.ts.
|
|
5
5
|
export const HERDR_PROTOCOL = 22;
|
|
6
6
|
// Effective accepted server protocols for K11: each undeclared bound means exactly the pin.
|
|
7
7
|
export function protocolBounds(range) {
|
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. */
|
|
@@ -53,6 +53,24 @@ export declare function herdrDevice(link: DeviceLink): {
|
|
|
53
53
|
tab?: string;
|
|
54
54
|
workspace?: string;
|
|
55
55
|
}): Promise<void>;
|
|
56
|
+
rename(o: {
|
|
57
|
+
pane?: string;
|
|
58
|
+
tab?: string;
|
|
59
|
+
workspace?: string;
|
|
60
|
+
label: string;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
focus(o: {
|
|
63
|
+
pane?: string;
|
|
64
|
+
tab?: string;
|
|
65
|
+
workspace?: string;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
layout(o: {
|
|
68
|
+
root: HerdrParams<'layout.apply'>['root'];
|
|
69
|
+
focus?: boolean;
|
|
70
|
+
tabId?: string;
|
|
71
|
+
tabLabel?: string;
|
|
72
|
+
workspaceId?: string;
|
|
73
|
+
}): Promise<HerdrResult<'layout.apply'>>;
|
|
56
74
|
events(): AsyncIterable<HerdrLinkEvent>;
|
|
57
75
|
subscribe<E extends HerdrEventName>(subs: HerdrSubscription<E>[], on: (e: HerdrEventOf<E>) => void, onError?: (message: string) => void): () => void;
|
|
58
76
|
registerNotices(seed: Uint8Array): Promise<void>;
|