pi-pier 0.3.0 → 0.4.1
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/README.md +16 -12
- package/package.json +3 -3
- package/schemas/role-manifest.schema.json +7 -1
- package/skills/pi-herdr-workflow/SKILL.md +4 -5
- package/src/ansi-text.ts +62 -11
- package/src/ask-dialog.ts +308 -0
- package/src/ask-user.ts +113 -112
- package/src/bootstrap.ts +10 -13
- package/src/compact-coordinator.ts +279 -261
- package/src/compact-economics-core.ts +77 -145
- package/src/config-catalog-core.ts +77 -197
- package/src/config-command.ts +271 -64
- package/src/cordis-services.ts +64 -0
- package/src/dashboard-command.ts +77 -33
- package/src/delegation-core.ts +115 -0
- package/src/efficiency-config-core.ts +286 -486
- package/src/efficiency-store.ts +16 -29
- package/src/gc-core.ts +10 -36
- package/src/guards.ts +6 -0
- package/src/herdr-client.ts +202 -233
- package/src/history-store.ts +38 -23
- package/src/index-gates.ts +118 -0
- package/src/index-locks.ts +2 -7
- package/src/index-master.ts +31 -29
- package/src/index-notices.ts +28 -15
- package/src/index-pipe.ts +31 -12
- package/src/index-roles.ts +306 -0
- package/src/index-runtime.ts +16 -6
- package/src/index-worker.ts +8 -18
- package/src/index.ts +561 -570
- package/src/jev-client.ts +12 -17
- package/src/jev-core.ts +138 -90
- package/src/ledger.ts +13 -21
- package/src/lock-core.ts +28 -56
- package/src/manifest-compose.ts +35 -20
- package/src/observation-core.ts +52 -100
- package/src/pane-title.ts +19 -38
- package/src/panel-open.ts +47 -0
- package/src/pi-surface.ts +34 -22
- package/src/pier-options.ts +53 -50
- package/src/pipe-channel.ts +44 -59
- package/src/platform-paths.ts +19 -52
- package/src/{core → plugins}/grid-shape.ts +8 -10
- package/src/{core → plugins}/heat-plan.ts +20 -22
- package/src/plugins/observation.ts +475 -0
- package/src/plugins/subagent.ts +766 -0
- package/src/{core → plugins}/terminal.ts +198 -139
- package/src/{core → plugins}/todo.ts +73 -134
- package/src/progress-core.ts +16 -53
- package/src/reconcile-core.ts +24 -33
- package/src/reducer-core.ts +26 -64
- package/src/reducer-invoker.ts +266 -239
- package/src/renderers.ts +108 -84
- package/src/role-loader.ts +33 -28
- package/src/role-manifest.ts +25 -31
- package/src/role-state.ts +119 -0
- package/src/roles/worker-default.json +3 -3
- package/src/routing-telemetry.ts +136 -0
- package/src/runtime-policy.ts +25 -40
- package/src/session-tail.ts +65 -41
- package/src/settle-wake-core.ts +61 -22
- package/src/slim-frame.ts +25 -122
- package/src/stale-core.ts +17 -24
- package/src/storage-layout.ts +14 -43
- package/src/subagent-core.ts +423 -120
- package/src/subagent-gc.ts +109 -67
- package/src/subagent-poller.ts +383 -24
- package/src/subagent-session.ts +516 -0
- package/src/subagent-spawn.ts +577 -52
- package/src/swallow.ts +7 -17
- package/src/terminal-core.ts +187 -134
- package/src/todo-core.ts +57 -109
- package/src/todo-read-hook.ts +46 -39
- package/src/todo-reminder-core.ts +26 -51
- package/src/todo-window.ts +11 -12
- package/src/todos-service.ts +15 -35
- package/src/tool-error.ts +6 -13
- package/src/tool-gate.ts +38 -21
- package/src/tree-view.ts +162 -0
- package/src/vocab.ts +52 -25
- package/src/worker-marker.ts +76 -0
- package/src/ask-multi.ts +0 -260
- package/src/config-guide.ts +0 -361
- package/src/core/observation.ts +0 -555
- package/src/core/subagent.ts +0 -633
- package/src/efficiency-config-io.ts +0 -53
- package/src/focus-poller.ts +0 -244
- package/src/git-adapter.ts +0 -113
- package/src/notice-buffer.ts +0 -23
- package/src/subagent-execute.ts +0 -328
- package/src/subagent-git-io.ts +0 -80
- package/src/subagent-launch.ts +0 -114
- package/src/subagent-list-action.ts +0 -69
- package/src/subagent-output-action.ts +0 -104
- package/src/subagent-output-adapter.ts +0 -27
- package/src/subagent-output-core.ts +0 -263
- package/src/subagent-poll-loop.ts +0 -305
- package/src/subagent-port.ts +0 -27
- package/src/subagent-ready.ts +0 -89
- package/src/subagent-registry.ts +0 -70
- package/src/subagent-resolution.ts +0 -41
- package/src/subagent-scope.ts +0 -41
- package/src/subagent-session-io.ts +0 -230
package/src/herdr-client.ts
CHANGED
|
@@ -1,37 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Minimal herdr socket API client used inside the pi extension.
|
|
3
3
|
*
|
|
4
|
-
* Validated against herdr 0.9.
|
|
5
|
-
* pier
|
|
6
|
-
* test/fixtures/herdr-contract.json
|
|
4
|
+
* Validated against herdr 0.9.5 (protocol 22; see WIRE.md — unchanged since 0.9.1; 0.9.2 removed only the
|
|
5
|
+
* pane.graphics.* API, which pier never used). The request shapes pier builds are pinned to the
|
|
6
|
+
* 0.9.5 methods snapshot in test/fixtures/herdr-contract.json (recent `api schema` releases no longer
|
|
7
|
+
* emit a methods map) — after a herdr upgrade, re-verify the protocol number and the method set, then
|
|
8
|
+
* regenerate the fixture and bump HERDR_PROTOCOL_EXPECTED when the server moves on:
|
|
7
9
|
* - Transport is NDJSON: request {id, method, params} → response {id, result} | {id, error:{code,message}};
|
|
8
10
|
* - **Control requests use one connection per request**; the server closes it after replying;
|
|
9
11
|
* - Windows targets are named pipes whose name is the complete socket_path with a \\.\pipe\ prefix;
|
|
10
|
-
* - pane.report_agent reports {pane_id, source, agent, state, message
|
|
11
|
-
*
|
|
12
|
-
* - pane.report_metadata reports {pane_id, source, title?, state_labels?,
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* - agent.list {} → {type:'agent_list', agents: AgentInfo[]};
|
|
16
|
-
* - agent.wait {target, until[], timeout_ms?} → the matching agent, or an error on timeout;
|
|
17
|
-
* - agent.send_keys {target, keys[]} / pane.send_text {pane_id, text}.
|
|
12
|
+
* - pane.report_agent reports {pane_id, source, agent, state, message?} where state ∈
|
|
13
|
+
* idle|working|blocked|unknown ('done' is server-derived and is not reported by the client);
|
|
14
|
+
* - pane.report_metadata reports {pane_id, source, title?, state_labels?, clear_*, tokens?, ttl_ms?}
|
|
15
|
+
* (ttl_ms is capped at 24 h by the server; the first report of a session also clears legacy
|
|
16
|
+
* pi-herdr chunk tokens to null).
|
|
18
17
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* (waitSettled marker polling, readPaneText parsing, onAgentState long-lived subscriptions)
|
|
22
|
-
* are replaced by session JSONL results (session-tail.ts) and server-side agent.wait.
|
|
23
|
-
*
|
|
24
|
-
* Design (DESIGN.md §4.1): without herdr, fall back to Noop so pi remains independent;
|
|
25
|
-
* failures in reporting calls stay silent and never affect the main pi flow.
|
|
18
|
+
* Reporting failures stay silent and never affect the main pi flow; without herdr the Noop client
|
|
19
|
+
* keeps pi independent (DESIGN.md §4.1).
|
|
26
20
|
*/
|
|
27
21
|
import { REPORT_AGENT_SOURCE, type TodoItem } from './vocab.ts';
|
|
28
22
|
import { BLOCKED_LABEL_KEY, SIDEBAR_ASK_TOKEN, formatBlockedLabel, formatPaneTitle, sidebarTodoTokens, staleTokenClearance } from './pane-title.ts';
|
|
23
|
+
import { applyWorkerTag, delegationSidebarTokens, type DelegationContext } from './delegation-core.ts';
|
|
29
24
|
import { LOCK_BATCH_LIMIT, LOCK_TTL_MS } from './lock-core.ts';
|
|
30
25
|
import * as net from 'node:net';
|
|
31
26
|
|
|
32
27
|
/* ── Types ──────────────────────────────────────────────────────────── */
|
|
33
28
|
|
|
34
|
-
/** Herdr agent semantic state; schema has five observed states and 'done' is server-derived. */
|
|
35
29
|
export type HerdrAgentState = 'idle' | 'working' | 'blocked' | 'done' | 'unknown';
|
|
36
30
|
|
|
37
31
|
export interface HerdrEnv {
|
|
@@ -66,6 +60,18 @@ export interface PaneListItem {
|
|
|
66
60
|
terminalTitleStripped?: string | null;
|
|
67
61
|
}
|
|
68
62
|
|
|
63
|
+
/** pane.process_info: the processes currently holding the pane's PTY foreground. */
|
|
64
|
+
export interface PaneProcessInfo {
|
|
65
|
+
paneId: string;
|
|
66
|
+
shellPid: number | null;
|
|
67
|
+
foreground: ReadonlyArray<{
|
|
68
|
+
name: string | null;
|
|
69
|
+
argv0: string | null;
|
|
70
|
+
pid: number | null;
|
|
71
|
+
cwd: string | null;
|
|
72
|
+
}>;
|
|
73
|
+
}
|
|
74
|
+
|
|
69
75
|
export interface PaneLayoutCell {
|
|
70
76
|
paneId: string;
|
|
71
77
|
x: number;
|
|
@@ -97,10 +103,11 @@ export interface OpenPluginPaneOptions {
|
|
|
97
103
|
|
|
98
104
|
export type OpenPluginPaneResult =
|
|
99
105
|
| { mode: 'popup'; ok: true }
|
|
106
|
+
/** Another popup/modal owns the session; the caller may switch that panel instead of opening a tab. */
|
|
107
|
+
| { mode: 'busy' }
|
|
100
108
|
| { mode: 'pane'; paneId: string }
|
|
101
109
|
| { mode: 'fallback_tab'; tabId?: string; paneId?: string };
|
|
102
110
|
|
|
103
|
-
/** TabInfo projection; fields mirror the observed tab schema. */
|
|
104
111
|
export interface TabInfo {
|
|
105
112
|
tabId: string;
|
|
106
113
|
workspaceId: string;
|
|
@@ -122,17 +129,13 @@ export function detectHerdrEnv(env: NodeJS.ProcessEnv = process.env): HerdrEnv |
|
|
|
122
129
|
};
|
|
123
130
|
}
|
|
124
131
|
|
|
125
|
-
/** Resolve herdr socket target (Windows named-pipe prefix vs Unix path). */
|
|
126
132
|
export function herdrSocketTarget(socketPath: string, platform: NodeJS.Platform = process.platform): string {
|
|
127
133
|
if (platform !== 'win32') return socketPath;
|
|
128
134
|
return socketPath.startsWith('\\\\.\\pipe\\') ? socketPath : `\\\\.\\pipe\\${socketPath}`;
|
|
129
135
|
}
|
|
130
136
|
|
|
131
|
-
/**
|
|
132
|
-
*
|
|
133
|
-
* A raw errno ("connect ENOENT /Users/…/herdr.sock") tells the model nothing to act on, so tool
|
|
134
|
-
* layers append this one sentence. Returns null when the failure is not a transport failure.
|
|
135
|
-
*/
|
|
137
|
+
/** B5: herdr configured but unreachable (stopped server, stale HERDR_SOCKET_PATH) — tool layers append
|
|
138
|
+
* this sentence because a raw errno tells the model nothing. Null when the failure is not transport. */
|
|
136
139
|
export function herdrUnavailableHint(err: unknown): string | null {
|
|
137
140
|
const msg = String((err as { message?: unknown })?.message ?? err);
|
|
138
141
|
if (!/ENOENT|ECONNREFUSED|ECONNRESET|EPIPE|ENOTCONN|not connected|socket|connect |timeout/i.test(msg)) {
|
|
@@ -141,82 +144,75 @@ export function herdrUnavailableHint(err: unknown): string | null {
|
|
|
141
144
|
return `herdr unreachable (${msg}) — check that the herdr server is running and HERDR_SOCKET_PATH matches its socket`;
|
|
142
145
|
}
|
|
143
146
|
|
|
144
|
-
|
|
145
|
-
/** Wire protocol this client is written against (herdr 0.9.1). See test/fixtures/herdr-contract.json. */
|
|
147
|
+
/** Wire protocol this client is written against (herdr 0.9.5, contract fixture regenerated 2026-10-20). */
|
|
146
148
|
export const HERDR_PROTOCOL_EXPECTED = 22;
|
|
147
149
|
|
|
148
150
|
export type WaitForOutputResult =
|
|
149
151
|
| { matched: true }
|
|
150
152
|
| { matched: false; reason: 'timeout' | 'unavailable' };
|
|
151
153
|
|
|
154
|
+
/** pane.read / agent.read selection: `recent` keeps ANSI so T6 fullscreen detection still sees it. */
|
|
155
|
+
export interface ReadOptions {
|
|
156
|
+
source?: 'visible' | 'recent' | 'recent_unwrapped';
|
|
157
|
+
lines?: number;
|
|
158
|
+
stripAnsi?: boolean;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Decoded read payload; `revision` is the server's buffer revision (0 when unreadable). */
|
|
162
|
+
export interface ReadBuffer {
|
|
163
|
+
text: string;
|
|
164
|
+
revision: number;
|
|
165
|
+
truncated: boolean;
|
|
166
|
+
}
|
|
167
|
+
|
|
152
168
|
export interface HerdrClientLike {
|
|
153
169
|
readonly available: boolean;
|
|
154
170
|
reportAgent(state: PaneAgentState, message: string | null): Promise<void>;
|
|
155
|
-
reportMetadata(meta: { session: string; items: readonly TodoItem[]; progressSuffix?: string | null; lastWriteAt?: number | null }): Promise<void>;
|
|
156
|
-
/** M18: report write-lock tokens (lock-<hash> → paneId|path); null releases, and batches stay within 16 keys. */
|
|
171
|
+
reportMetadata(meta: { session: string; items: readonly TodoItem[]; progressSuffix?: string | null; lastWriteAt?: number | null; workerTag?: string | null }): Promise<void>;
|
|
157
172
|
reportLockTokens(tokens: Record<string, string | null>): Promise<void>;
|
|
158
|
-
/**
|
|
159
|
-
* D93: sidebar agent display name (display_agent is the role name).
|
|
160
|
-
* The report persists without a TTL; null clears it back to the detected value. Best effort.
|
|
161
|
-
*/
|
|
173
|
+
/** D93: sidebar agent display name (display_agent is the role name); null clears it. Persists, no TTL. */
|
|
162
174
|
reportDisplayAgent(name: string | null): Promise<void>;
|
|
163
175
|
/**
|
|
164
|
-
* D95: ask_user_question waiting marker (tokens['pi-ask']; null clears it).
|
|
165
|
-
*
|
|
176
|
+
* D95: ask_user_question waiting marker (tokens['pi-ask']; null clears it). The sidebar/heatmap
|
|
177
|
+
* grade blocked + pi-ask as the ask level, distinct from plain block.
|
|
166
178
|
*/
|
|
167
179
|
reportAskFlag(text: string | null): Promise<void>;
|
|
180
|
+
/** Delegation identity tokens (pi-parent/pi-task): who delegated this pane and for what task. */
|
|
181
|
+
reportDelegationTokens(d: DelegationContext | null): Promise<void>;
|
|
182
|
+
/**
|
|
183
|
+
* herdr 0.9.2+ self-reported restore: register the command that reopens this exact session after
|
|
184
|
+
* a server restart. Worker panes only; masters stay with herdr's native pi integration.
|
|
185
|
+
* Silently no-ops on older servers and on any error — resume is always best-effort.
|
|
186
|
+
*/
|
|
187
|
+
reportAgentSessionResume(sessionId: string, resumeArgv: readonly string[]): Promise<void>;
|
|
168
188
|
listAgents(): Promise<AgentInfo[]>;
|
|
169
|
-
/**
|
|
170
|
-
spawnSubPane(opts: { label: string; command: string[]; cwd: string; env?: Record<string, string> }): Promise<{ tabId: string; paneId: string }>;
|
|
171
|
-
/** Inject text into a pane terminal (pi editor input + Enter); send_text+CR reaches it directly and is used only to start launchLine. */
|
|
189
|
+
/** Inject text into a pane terminal (pi editor input + Enter); the PTY channel is used only to start launchLine. */
|
|
172
190
|
sendPaneText(paneId: string, text: string): Promise<void>;
|
|
173
|
-
/**
|
|
191
|
+
/** Server-side wait; null on timeout, throws on any other error. */
|
|
174
192
|
waitAgent(paneId: string, until: HerdrAgentState[], timeoutMs: number): Promise<HerdrAgentState | null>;
|
|
175
193
|
/** Query the child-agent session path (agent_session.value, kind=path); return null when absent. */
|
|
176
194
|
getAgentSessionPath(paneId: string): Promise<string | null>;
|
|
177
|
-
/** v1.2: Focus a pane before adding it to a group tab. */
|
|
178
|
-
focusPane(paneId: string): Promise<void>;
|
|
179
|
-
/** v1.2: Split a new shell pane in the current tab after focus, placing it in the group tab; return its pane ID. */
|
|
180
195
|
splitPane(opts: { direction?: 'left' | 'right' | 'up' | 'down'; cwd?: string; env?: Record<string, string>; targetPaneId?: string; focus?: boolean; ratio?: number }): Promise<string>;
|
|
181
|
-
/**
|
|
196
|
+
/** Close a pane; herdr kills its process tree and closes an empty tab. */
|
|
182
197
|
closePane(paneId: string): Promise<void>;
|
|
183
|
-
/** v1.2: Create a tab with a root shell pane, returning tabId/paneId for group-tab infrastructure. */
|
|
184
198
|
createTab(opts: { workspaceId: string; label?: string; cwd?: string; env?: Record<string, string> }): Promise<{ tabId: string; paneId: string }>;
|
|
185
|
-
/** v1.2: List all panes with tab ownership for group-tab additions. */
|
|
186
199
|
listPanes(): Promise<PaneListItem[]>;
|
|
187
|
-
/**
|
|
200
|
+
/** Foreground process snapshot; null when the server predates the method or the pane is gone. */
|
|
201
|
+
processInfo(paneId: string): Promise<PaneProcessInfo | null>;
|
|
202
|
+
/** D91: layout tree by paneId or tabId; null on failure. */
|
|
188
203
|
exportLayout(opts?: { paneId?: string; tabId?: string }): Promise<{ tabId: string | null; zoomed: boolean; root: unknown; focusedPaneId: string | null } | null>;
|
|
189
204
|
/** 0.9.1: live pane rects for the tab containing paneId. Best effort returns null. */
|
|
190
205
|
paneLayout(opts?: { paneId?: string }): Promise<PaneLayoutSnapshot | null>;
|
|
191
|
-
/** v1.3: List tabs (tab.list); fields follow the observed schema. */
|
|
192
206
|
tabList(): Promise<TabInfo[]>;
|
|
193
|
-
/** v1.3:
|
|
194
|
-
tabGet(tabId: string): Promise<TabInfo | null>;
|
|
195
|
-
/** v1.3: Close a tab (tab.close), cascading to its panes. */
|
|
207
|
+
/** v1.3: tab.close cascades to the tab's panes. */
|
|
196
208
|
tabClose(tabId: string): Promise<void>;
|
|
197
209
|
/** 0.9.1: Launch a manifest pane entrypoint with placement (popup/tab/split/overlay/zoomed). */
|
|
198
210
|
openPluginPane(opts: OpenPluginPaneOptions): Promise<OpenPluginPaneResult>;
|
|
199
|
-
/** 0.9.1: Close active popup if open; returns false if no popup was open. */
|
|
200
|
-
closePopup(): Promise<boolean>;
|
|
201
|
-
/** 0.9.1: Query agent explain diagnostics. */
|
|
202
211
|
agentExplain(target: string): Promise<Record<string, unknown> | null>;
|
|
203
|
-
/**
|
|
204
|
-
getServerVersion(): Promise<string | null>;
|
|
205
|
-
/** M14: Send a key combination (pane.send_keys; Herdr key combos include ctrl+c, enter, and esc). */
|
|
212
|
+
/** pane.send_keys; herdr key combos include ctrl+c, enter, and esc. */
|
|
206
213
|
sendPaneKeys(paneId: string, keys: string[]): Promise<void>;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
source?: 'visible' | 'recent' | 'recent_unwrapped';
|
|
210
|
-
lines?: number;
|
|
211
|
-
stripAnsi?: boolean;
|
|
212
|
-
}): Promise<{ text: string; revision: number; truncated: boolean }>;
|
|
213
|
-
/** Read the agent output buffer via herdr 0.9 agent.read RPC. */
|
|
214
|
-
readAgent(target: string, opts?: {
|
|
215
|
-
source?: 'visible' | 'recent' | 'recent_unwrapped';
|
|
216
|
-
lines?: number;
|
|
217
|
-
stripAnsi?: boolean;
|
|
218
|
-
}): Promise<{ text: string; revision: number; truncated: boolean }>;
|
|
219
|
-
/** M14: Wait for output to match a substring or regex; return discriminated result on match/timeout/unavailable. */
|
|
214
|
+
readPane(paneId: string, opts?: ReadOptions): Promise<ReadBuffer>;
|
|
215
|
+
readAgent(target: string, opts?: ReadOptions): Promise<ReadBuffer>;
|
|
220
216
|
waitForOutput(paneId: string, match: { type: 'substring' | 'regex'; value: string }, timeoutMs: number): Promise<WaitForOutputResult>;
|
|
221
217
|
close(): void;
|
|
222
218
|
}
|
|
@@ -237,73 +233,36 @@ function findIdIn(obj: unknown, key: string, depth = 0): string | null {
|
|
|
237
233
|
|
|
238
234
|
/* ── Noop ──────────────────────────────────────────────────────────── */
|
|
239
235
|
|
|
236
|
+
/** Stand-in outside a herdr pane: queries answer empty, pane creation throws, reporting is a no-op. */
|
|
240
237
|
export class NoopHerdrClient implements HerdrClientLike {
|
|
241
238
|
readonly available = false;
|
|
242
239
|
async reportAgent(): Promise<void> {}
|
|
240
|
+
async reportAgentSessionResume(): Promise<void> {}
|
|
243
241
|
async reportMetadata(): Promise<void> {}
|
|
244
242
|
async reportLockTokens(): Promise<void> {}
|
|
243
|
+
async reportDelegationTokens(): Promise<void> {}
|
|
245
244
|
async reportDisplayAgent(): Promise<void> {}
|
|
246
245
|
async reportAskFlag(): Promise<void> {}
|
|
247
|
-
async listAgents(): Promise<AgentInfo[]> {
|
|
248
|
-
return [];
|
|
249
|
-
}
|
|
250
|
-
async spawnSubPane(): Promise<{ tabId: string; paneId: string }> {
|
|
251
|
-
throw new Error('subagent requires a herdr-managed pane');
|
|
252
|
-
}
|
|
253
246
|
async sendPaneText(): Promise<void> {}
|
|
254
|
-
async waitAgent(): Promise<null> {
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
async getAgentSessionPath(): Promise<null> {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
async focusPane(): Promise<void> {}
|
|
261
|
-
async splitPane(): Promise<string> {
|
|
262
|
-
throw new Error('subagent requires a herdr-managed pane');
|
|
263
|
-
}
|
|
264
247
|
async closePane(): Promise<void> {}
|
|
265
|
-
async createTab(): Promise<{ tabId: string; paneId: string }> {
|
|
266
|
-
throw new Error('subagent requires a herdr-managed pane');
|
|
267
|
-
}
|
|
268
|
-
async listPanes(): Promise<PaneListItem[]> {
|
|
269
|
-
return [];
|
|
270
|
-
}
|
|
271
|
-
async exportLayout(): Promise<null> {
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
async paneLayout(): Promise<null> {
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
async tabList(): Promise<TabInfo[]> {
|
|
278
|
-
return [];
|
|
279
|
-
}
|
|
280
|
-
async tabGet(): Promise<TabInfo | null> {
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
283
248
|
async tabClose(): Promise<void> {}
|
|
284
|
-
async openPluginPane(): Promise<OpenPluginPaneResult> {
|
|
285
|
-
throw new Error('plugin pane requires a herdr-managed pane');
|
|
286
|
-
}
|
|
287
|
-
async closePopup(): Promise<boolean> {
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
async agentExplain(): Promise<null> {
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
async getServerVersion(): Promise<null> {
|
|
294
|
-
return null;
|
|
295
|
-
}
|
|
296
249
|
async sendPaneKeys(): Promise<void> {}
|
|
297
|
-
async readPane(): Promise<{ text: string; revision: number; truncated: boolean }> {
|
|
298
|
-
return { text: '', revision: 0, truncated: false };
|
|
299
|
-
}
|
|
300
|
-
async readAgent(): Promise<{ text: string; revision: number; truncated: boolean }> {
|
|
301
|
-
return { text: '', revision: 0, truncated: false };
|
|
302
|
-
}
|
|
303
|
-
async waitForOutput(): Promise<WaitForOutputResult> {
|
|
304
|
-
return { matched: false, reason: 'unavailable' };
|
|
305
|
-
}
|
|
306
250
|
close(): void {}
|
|
251
|
+
async listAgents(): Promise<AgentInfo[]> { return []; }
|
|
252
|
+
async listPanes(): Promise<PaneListItem[]> { return []; }
|
|
253
|
+
async processInfo(): Promise<PaneProcessInfo | null> { return null; }
|
|
254
|
+
async tabList(): Promise<TabInfo[]> { return []; }
|
|
255
|
+
async waitAgent(): Promise<null> { return null; }
|
|
256
|
+
async getAgentSessionPath(): Promise<null> { return null; }
|
|
257
|
+
async exportLayout(): Promise<null> { return null; }
|
|
258
|
+
async paneLayout(): Promise<null> { return null; }
|
|
259
|
+
async agentExplain(): Promise<null> { return null; }
|
|
260
|
+
async readPane(): Promise<ReadBuffer> { return { text: '', revision: 0, truncated: false }; }
|
|
261
|
+
async readAgent(): Promise<ReadBuffer> { return { text: '', revision: 0, truncated: false }; }
|
|
262
|
+
async waitForOutput(): Promise<WaitForOutputResult> { return { matched: false, reason: 'unavailable' }; }
|
|
263
|
+
async splitPane(): Promise<string> { throw new Error('subagent requires a herdr-managed pane'); }
|
|
264
|
+
async createTab(): Promise<{ tabId: string; paneId: string }> { throw new Error('subagent requires a herdr-managed pane'); }
|
|
265
|
+
async openPluginPane(): Promise<OpenPluginPaneResult> { throw new Error('plugin pane requires a herdr-managed pane'); }
|
|
307
266
|
}
|
|
308
267
|
|
|
309
268
|
/* ── Implementation ────────────────────────────────────────────────── */
|
|
@@ -321,10 +280,7 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
321
280
|
return herdrSocketTarget(this.env.socketPath);
|
|
322
281
|
}
|
|
323
282
|
|
|
324
|
-
/**
|
|
325
|
-
* Control requests use one connection per request; the server closes it after replying, as observed in the protocol.
|
|
326
|
-
* Reading a complete response line finishes the request; responses are {id, result} or {id, error}.
|
|
327
|
-
*/
|
|
283
|
+
/** One connection per control request: the reply is a single {id,result}|{id,error} line. */
|
|
328
284
|
private request(method: string, params: Record<string, unknown>, timeoutMs = 15000): Promise<unknown> {
|
|
329
285
|
return new Promise((resolve, reject) => {
|
|
330
286
|
const sock = net.createConnection(this.target());
|
|
@@ -383,6 +339,13 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
383
339
|
|
|
384
340
|
/* ── Reporting (silent best effort: the workbench mirror must never affect pi's main flow) ── */
|
|
385
341
|
|
|
342
|
+
/**
|
|
343
|
+
* D-2 (verified 2026-10-20): herdr's native `herdr:pi` integration owns `agent_status`
|
|
344
|
+
* (`screen_detection_skip_reason: full_lifecycle_hook_authority`), but it never reports the
|
|
345
|
+
* activity text. `pane.report_agent` is still the only carrier for the tool badge, so pier keeps
|
|
346
|
+
* sending state with the message; removing it requires migrating the badge to
|
|
347
|
+
* `report_metadata.state_labels` first.
|
|
348
|
+
*/
|
|
386
349
|
async reportAgent(state: PaneAgentState, message: string | null): Promise<void> {
|
|
387
350
|
try {
|
|
388
351
|
await this.request('pane.report_agent', {
|
|
@@ -393,24 +356,53 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
393
356
|
...(message ? { message } : {}),
|
|
394
357
|
});
|
|
395
358
|
} catch {
|
|
396
|
-
/* Silent best effort. */
|
|
397
359
|
}
|
|
398
360
|
}
|
|
399
361
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Master panes stay with herdr's native pi integration (it owns the session path); workers
|
|
364
|
+
* self-report so a restart replays pier's own launch line — extension loaded, role model, approve,
|
|
365
|
+
* fullscreen frame — instead of a bare `pi --resume` without `-e`. herdr requires the source to
|
|
366
|
+
* hold the pane before accepting a resume command (resume_not_accepted), so an idle state report
|
|
367
|
+
* goes first, with one retry to tolerate a lost race with the session_start gate report.
|
|
368
|
+
*/
|
|
369
|
+
async reportAgentSessionResume(sessionId: string, resumeArgv: readonly string[]): Promise<void> {
|
|
370
|
+
if (!this.env) return;
|
|
371
|
+
try {
|
|
372
|
+
await this.reportAgent('idle', null);
|
|
373
|
+
const params = {
|
|
374
|
+
pane_id: this.env.paneId,
|
|
375
|
+
source: REPORT_AGENT_SOURCE,
|
|
376
|
+
agent: 'pi',
|
|
377
|
+
agent_session_id: sessionId,
|
|
378
|
+
resume_argv: [...resumeArgv],
|
|
379
|
+
};
|
|
380
|
+
try {
|
|
381
|
+
await this.request('pane.report_agent_session', params);
|
|
382
|
+
} catch {
|
|
383
|
+
const { promise: retryDelay, resolve: retryNow } = Promise.withResolvers<void>();
|
|
384
|
+
setTimeout(retryNow, 250);
|
|
385
|
+
await retryDelay;
|
|
386
|
+
await this.request('pane.report_agent_session', params);
|
|
387
|
+
}
|
|
388
|
+
} catch {
|
|
389
|
+
/* best effort: live sessions never depend on restore metadata */
|
|
390
|
+
}
|
|
391
|
+
}
|
|
403
392
|
|
|
404
|
-
async reportMetadata(meta: { session: string; items: readonly TodoItem[]; progressSuffix?: string | null; lastWriteAt?: number | null }): Promise<void> {
|
|
393
|
+
async reportMetadata(meta: { session: string; items: readonly TodoItem[]; progressSuffix?: string | null; lastWriteAt?: number | null; workerTag?: string | null }): Promise<void> {
|
|
405
394
|
try {
|
|
406
395
|
void meta.session;
|
|
407
|
-
const title =
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
396
|
+
const title = applyWorkerTag(
|
|
397
|
+
formatPaneTitle(meta.items, null, {
|
|
398
|
+
progressSuffix: meta.progressSuffix,
|
|
399
|
+
lastWriteAt: meta.lastWriteAt,
|
|
400
|
+
}),
|
|
401
|
+
meta.workerTag ?? null,
|
|
402
|
+
);
|
|
411
403
|
const blocked = formatBlockedLabel(meta.items);
|
|
412
|
-
// D96:
|
|
413
|
-
//
|
|
404
|
+
// D96: stale cleanup needs its own batch — stale(16) + pi-todo(1) exceeds herdr's 16-token
|
|
405
|
+
// limit, and the rejected request would drop both the title and the tokens.
|
|
414
406
|
if (!this.clearedStaleTokens) {
|
|
415
407
|
await this.request('pane.report_metadata', {
|
|
416
408
|
pane_id: this.env.paneId,
|
|
@@ -427,12 +419,27 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
427
419
|
...(blocked
|
|
428
420
|
? { state_labels: { [BLOCKED_LABEL_KEY]: blocked } }
|
|
429
421
|
: { clear_state_labels: true }),
|
|
430
|
-
// D93:
|
|
422
|
+
// D93: the todo summary is a custom token; an empty string clears the key instead of leaving a stale summary.
|
|
431
423
|
tokens: sidebarTodoTokens(title),
|
|
432
424
|
ttl_ms: 86400000,
|
|
433
425
|
});
|
|
434
426
|
} catch {
|
|
435
|
-
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/** RFC rfc-delegation-identity §2.3: separate batch — tokens patches never merge with the stale
|
|
431
|
+
* cleanup or the todo title (D96 discipline); an unknown parent pane skips the report. */
|
|
432
|
+
async reportDelegationTokens(d: DelegationContext | null): Promise<void> {
|
|
433
|
+
const tokens = d ? delegationSidebarTokens(d) : {};
|
|
434
|
+
if (Object.keys(tokens).length === 0) return;
|
|
435
|
+
try {
|
|
436
|
+
await this.request('pane.report_metadata', {
|
|
437
|
+
pane_id: this.env.paneId,
|
|
438
|
+
source: REPORT_AGENT_SOURCE,
|
|
439
|
+
tokens,
|
|
440
|
+
ttl_ms: 86400000,
|
|
441
|
+
});
|
|
442
|
+
} catch {
|
|
436
443
|
}
|
|
437
444
|
}
|
|
438
445
|
|
|
@@ -440,7 +447,6 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
440
447
|
const keys = Object.keys(tokens);
|
|
441
448
|
if (keys.length === 0) return;
|
|
442
449
|
try {
|
|
443
|
-
// schema maxProperties=16, so split into independent best-effort requests.
|
|
444
450
|
for (let i = 0; i < keys.length; i += LOCK_BATCH_LIMIT) {
|
|
445
451
|
const batch: Record<string, string | null> = {};
|
|
446
452
|
for (const k of keys.slice(i, i + LOCK_BATCH_LIMIT)) batch[k] = tokens[k];
|
|
@@ -464,7 +470,6 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
464
470
|
...(name ? { display_agent: name } : { clear_display_agent: true }),
|
|
465
471
|
});
|
|
466
472
|
} catch {
|
|
467
|
-
/* Silent best effort: sidebar identity is supplementary. */
|
|
468
473
|
}
|
|
469
474
|
}
|
|
470
475
|
|
|
@@ -477,7 +482,6 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
477
482
|
ttl_ms: 86400000,
|
|
478
483
|
});
|
|
479
484
|
} catch {
|
|
480
|
-
/* Silent best effort: the human-gate marker is supplementary. */
|
|
481
485
|
}
|
|
482
486
|
}
|
|
483
487
|
|
|
@@ -503,16 +507,6 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
503
507
|
});
|
|
504
508
|
}
|
|
505
509
|
|
|
506
|
-
async spawnSubPane(opts: { label: string; command: string[]; cwd: string; env?: Record<string, string> }): Promise<{ tabId: string; paneId: string }> {
|
|
507
|
-
const result = (await this.request('layout.apply', {
|
|
508
|
-
tab_label: opts.label,
|
|
509
|
-
root: { type: 'pane', command: opts.command, cwd: opts.cwd, ...(opts.env ? { env: opts.env } : {}) },
|
|
510
|
-
})) as Record<string, unknown>;
|
|
511
|
-
const paneId = findIdIn(result, 'pane_id');
|
|
512
|
-
if (!paneId) throw new Error('layout.apply: no pane_id in response');
|
|
513
|
-
return { tabId: findIdIn(result, 'tab_id') ?? '', paneId };
|
|
514
|
-
}
|
|
515
|
-
|
|
516
510
|
async sendPaneText(paneId: string, text: string): Promise<void> {
|
|
517
511
|
// A trailing \r acts as Enter; observed behavior sends text to the pi editor and submits it.
|
|
518
512
|
await this.request('pane.send_text', { pane_id: paneId, text: text + '\r' });
|
|
@@ -531,8 +525,7 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
531
525
|
const msg = String((err as Error)?.message ?? err);
|
|
532
526
|
if (/timeout/i.test(msg)) return null;
|
|
533
527
|
// A14: herdr reports agent_not_found while a freshly spawned pane's agent is still registering;
|
|
534
|
-
// callers poll
|
|
535
|
-
// failing the whole spawn with a raw server error.
|
|
528
|
+
// callers poll, so map it to the same "nothing yet" result as a timeout.
|
|
536
529
|
if (/agent_not_found|agent target .* not found/i.test(msg)) return null;
|
|
537
530
|
throw err;
|
|
538
531
|
}
|
|
@@ -544,10 +537,6 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
544
537
|
return a?.session ?? null;
|
|
545
538
|
}
|
|
546
539
|
|
|
547
|
-
async focusPane(paneId: string): Promise<void> {
|
|
548
|
-
await this.request('pane.focus', { pane_id: paneId });
|
|
549
|
-
}
|
|
550
|
-
|
|
551
540
|
async splitPane(opts: { direction?: 'left' | 'right' | 'up' | 'down'; cwd?: string; env?: Record<string, string>; targetPaneId?: string; focus?: boolean; ratio?: number } = {}): Promise<string> {
|
|
552
541
|
const params: Record<string, unknown> = {
|
|
553
542
|
direction: opts.direction ?? 'right',
|
|
@@ -605,6 +594,29 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
605
594
|
}));
|
|
606
595
|
}
|
|
607
596
|
|
|
597
|
+
async processInfo(paneId: string): Promise<PaneProcessInfo | null> {
|
|
598
|
+
try {
|
|
599
|
+
const result = (await this.request('pane.process_info', { pane_id: paneId })) as Record<string, unknown> | null;
|
|
600
|
+
const info = (result?.process_info ?? result) as Record<string, unknown> | null | undefined;
|
|
601
|
+
if (!info || typeof info !== 'object') return null;
|
|
602
|
+
const raw = Array.isArray(info.foreground_processes) ? info.foreground_processes : [];
|
|
603
|
+
return {
|
|
604
|
+
paneId: typeof info.pane_id === 'string' ? info.pane_id : paneId,
|
|
605
|
+
shellPid: typeof info.shell_pid === 'number' ? info.shell_pid : null,
|
|
606
|
+
foreground: raw
|
|
607
|
+
.filter((p): p is Record<string, unknown> => p !== null && typeof p === 'object')
|
|
608
|
+
.map((p) => ({
|
|
609
|
+
name: typeof p.name === 'string' ? p.name : null,
|
|
610
|
+
argv0: typeof p.argv0 === 'string' ? p.argv0 : null,
|
|
611
|
+
pid: typeof p.pid === 'number' ? p.pid : null,
|
|
612
|
+
cwd: typeof p.cwd === 'string' ? p.cwd : null,
|
|
613
|
+
})),
|
|
614
|
+
};
|
|
615
|
+
} catch {
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
608
620
|
async exportLayout(opts: { paneId?: string; tabId?: string } = {}): Promise<{ tabId: string | null; zoomed: boolean; root: unknown; focusedPaneId: string | null } | null> {
|
|
609
621
|
try {
|
|
610
622
|
const result = (await this.request('layout.export', {
|
|
@@ -676,11 +688,6 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
676
688
|
return (result?.tabs ?? []).map((t) => this.mapTabInfo(t)).filter((t): t is TabInfo => t !== null);
|
|
677
689
|
}
|
|
678
690
|
|
|
679
|
-
async tabGet(tabId: string): Promise<TabInfo | null> {
|
|
680
|
-
const result = (await this.request('tab.get', { tab_id: tabId })) as { tab?: Record<string, unknown> | null } | null;
|
|
681
|
-
return this.mapTabInfo(result?.tab ?? null);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
691
|
async tabClose(tabId: string): Promise<void> {
|
|
685
692
|
await this.request('tab.close', { tab_id: tabId });
|
|
686
693
|
}
|
|
@@ -705,18 +712,17 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
705
712
|
}
|
|
706
713
|
return { mode: 'pane', paneId: findIdIn(result, 'pane_id') ?? '' };
|
|
707
714
|
} catch (err) {
|
|
715
|
+
// A live popup/modal is a transient state, not an old server: opening a tab would orphan a
|
|
716
|
+
// second dashboard and silently change the surface. Callers decide whether to switch panels.
|
|
717
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
718
|
+
if (opts.placement === 'popup' && /ui_busy|popup pane is already open/i.test(message)) {
|
|
719
|
+
return { mode: 'busy' };
|
|
720
|
+
}
|
|
708
721
|
// 0.9.0 serde rejects unknown `popup` as "unknown variant" / invalid_params — not a dedicated
|
|
709
|
-
// invalid_placement code. Any popup failure retries as tab; tab failure surfaces to Level 3.
|
|
722
|
+
// invalid_placement code. Any other popup failure retries as tab; tab failure surfaces to Level 3.
|
|
710
723
|
if (opts.placement !== 'popup') throw err;
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
entrypoint: opts.entrypoint,
|
|
714
|
-
placement: 'tab',
|
|
715
|
-
...(opts.focus !== undefined ? { focus: opts.focus } : {}),
|
|
716
|
-
...(opts.cwd ? { cwd: opts.cwd } : {}),
|
|
717
|
-
...(opts.env ? { env: opts.env } : {}),
|
|
718
|
-
...(opts.workspaceId ? { workspace_id: opts.workspaceId } : {}),
|
|
719
|
-
})) as Record<string, unknown> | null;
|
|
724
|
+
const { width: _width, height: _height, target_pane_id: _target, ...base } = params;
|
|
725
|
+
const fallbackRes = (await this.request('plugin.pane.open', { ...base, placement: 'tab' })) as Record<string, unknown> | null;
|
|
720
726
|
return {
|
|
721
727
|
mode: 'fallback_tab',
|
|
722
728
|
paneId: findIdIn(fallbackRes, 'pane_id') ?? undefined,
|
|
@@ -725,19 +731,6 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
725
731
|
}
|
|
726
732
|
}
|
|
727
733
|
|
|
728
|
-
async closePopup(): Promise<boolean> {
|
|
729
|
-
try {
|
|
730
|
-
await this.request('popup.close', {});
|
|
731
|
-
return true;
|
|
732
|
-
} catch (err) {
|
|
733
|
-
const msg = String((err as Error)?.message ?? err);
|
|
734
|
-
if (/popup_not_open|not found|not supported|unknown method/i.test(msg)) {
|
|
735
|
-
return false;
|
|
736
|
-
}
|
|
737
|
-
throw err;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
734
|
async agentExplain(target: string): Promise<Record<string, unknown> | null> {
|
|
742
735
|
try {
|
|
743
736
|
const result = (await this.request('agent.explain', { target })) as Record<string, unknown> | null;
|
|
@@ -752,39 +745,27 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
752
745
|
}
|
|
753
746
|
}
|
|
754
747
|
|
|
755
|
-
private cachedVersion: string | null = null;
|
|
756
|
-
|
|
757
|
-
async getServerVersion(): Promise<string | null> {
|
|
758
|
-
if (this.cachedVersion) return this.cachedVersion;
|
|
759
|
-
try {
|
|
760
|
-
const result = (await this.request('ping', {})) as { version?: unknown } | null;
|
|
761
|
-
if (typeof result?.version === 'string') {
|
|
762
|
-
this.cachedVersion = result.version;
|
|
763
|
-
return result.version;
|
|
764
|
-
}
|
|
765
|
-
} catch {
|
|
766
|
-
/* ignore */
|
|
767
|
-
}
|
|
768
|
-
return null;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
748
|
async sendPaneKeys(paneId: string, keys: string[]): Promise<void> {
|
|
772
749
|
await this.request('pane.send_keys', { pane_id: paneId, keys });
|
|
773
750
|
}
|
|
774
751
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
752
|
+
/** Shared pane.read/agent.read decode: payload nested under `read` (older servers answer it flat). */
|
|
753
|
+
private async readBuffer(
|
|
754
|
+
method: 'pane.read' | 'agent.read',
|
|
755
|
+
target: { pane_id: string } | { target: string },
|
|
756
|
+
opts: ReadOptions,
|
|
757
|
+
stripAnsiDefault: boolean,
|
|
758
|
+
): Promise<ReadBuffer> {
|
|
759
|
+
const result = (await this.request(method, {
|
|
760
|
+
...target,
|
|
783
761
|
source: opts.source ?? 'recent',
|
|
784
762
|
format: 'text',
|
|
785
|
-
strip_ansi: opts.stripAnsi ??
|
|
763
|
+
strip_ansi: opts.stripAnsi ?? stripAnsiDefault,
|
|
786
764
|
...(opts.lines != null ? { lines: opts.lines } : {}),
|
|
787
|
-
})) as {
|
|
765
|
+
})) as {
|
|
766
|
+
read?: { text?: unknown; revision?: unknown; truncated?: unknown };
|
|
767
|
+
text?: unknown; revision?: unknown; truncated?: unknown;
|
|
768
|
+
} | null;
|
|
788
769
|
const payload = result?.read ?? result ?? {};
|
|
789
770
|
return {
|
|
790
771
|
text: typeof payload.text === 'string' ? payload.text : '',
|
|
@@ -793,24 +774,12 @@ export class HerdrClient implements HerdrClientLike {
|
|
|
793
774
|
};
|
|
794
775
|
}
|
|
795
776
|
|
|
796
|
-
async
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
target,
|
|
803
|
-
source: opts.source ?? 'recent',
|
|
804
|
-
format: 'text',
|
|
805
|
-
strip_ansi: opts.stripAnsi ?? true,
|
|
806
|
-
...(opts.lines != null ? { lines: opts.lines } : {}),
|
|
807
|
-
})) as { read?: { text?: unknown; revision?: unknown; truncated?: unknown }; text?: unknown; revision?: unknown; truncated?: unknown } | null;
|
|
808
|
-
const payload = result?.read ?? result ?? {};
|
|
809
|
-
return {
|
|
810
|
-
text: typeof payload.text === 'string' ? payload.text : '',
|
|
811
|
-
revision: typeof payload.revision === 'number' ? payload.revision : 0,
|
|
812
|
-
truncated: payload.truncated === true,
|
|
813
|
-
};
|
|
777
|
+
async readPane(paneId: string, opts: ReadOptions = {}): Promise<ReadBuffer> {
|
|
778
|
+
return this.readBuffer('pane.read', { pane_id: paneId }, opts, false);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
async readAgent(target: string, opts: ReadOptions = {}): Promise<ReadBuffer> {
|
|
782
|
+
return this.readBuffer('agent.read', { target }, opts, true);
|
|
814
783
|
}
|
|
815
784
|
|
|
816
785
|
async waitForOutput(paneId: string, match: { type: 'substring' | 'regex'; value: string }, timeoutMs: number): Promise<WaitForOutputResult> {
|