@geohar/un-bien 0.9.0 → 0.14.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.
Files changed (136) hide show
  1. package/README.md +99 -130
  2. package/dist/actions/handlers.js.map +1 -1
  3. package/dist/actions/registry.js.map +1 -1
  4. package/dist/bin/launcher.js.map +1 -1
  5. package/dist/commands/deps.d.ts +156 -0
  6. package/dist/commands/deps.js +2 -0
  7. package/dist/commands/deps.js.map +1 -0
  8. package/dist/commands/fork_link.d.ts +4 -0
  9. package/dist/commands/fork_link.js +45 -0
  10. package/dist/commands/fork_link.js.map +1 -0
  11. package/dist/commands/housekeeping.d.ts +34 -0
  12. package/dist/commands/housekeeping.js +275 -0
  13. package/dist/commands/housekeeping.js.map +1 -0
  14. package/dist/commands/info.d.ts +43 -0
  15. package/dist/commands/info.js +127 -0
  16. package/dist/commands/info.js.map +1 -0
  17. package/dist/commands/lifecycle.d.ts +45 -0
  18. package/dist/commands/lifecycle.js +708 -0
  19. package/dist/commands/lifecycle.js.map +1 -0
  20. package/dist/commands/pairing.d.ts +26 -0
  21. package/dist/commands/pairing.js +167 -0
  22. package/dist/commands/pairing.js.map +1 -0
  23. package/dist/commands/register.d.ts +12 -0
  24. package/dist/commands/register.js +255 -0
  25. package/dist/commands/register.js.map +1 -0
  26. package/dist/commands/relay.d.ts +28 -0
  27. package/dist/commands/relay.js +81 -0
  28. package/dist/commands/relay.js.map +1 -0
  29. package/dist/commands/session_ops.d.ts +52 -0
  30. package/dist/commands/session_ops.js +75 -0
  31. package/dist/commands/session_ops.js.map +1 -0
  32. package/dist/config.d.ts +2 -1
  33. package/dist/config.js.map +1 -1
  34. package/dist/daemon/install.d.ts +4 -3
  35. package/dist/daemon/install.js +7 -1
  36. package/dist/daemon/install.js.map +1 -1
  37. package/dist/enrich_tool_args.js.map +1 -1
  38. package/dist/extension_ui_bridge.js.map +1 -1
  39. package/dist/image_codec.js.map +1 -1
  40. package/dist/index.d.ts +32 -101
  41. package/dist/index.js +818 -2867
  42. package/dist/index.js.map +1 -1
  43. package/dist/launch.d.ts +9 -1
  44. package/dist/launch.js +39 -4
  45. package/dist/launch.js.map +1 -1
  46. package/dist/launcher/launcher.js +21 -2
  47. package/dist/launcher/launcher.js.map +1 -1
  48. package/dist/mcp/mesh_result.js.map +1 -1
  49. package/dist/mcp/mesh_server.js.map +1 -1
  50. package/dist/mesh/canonical.js +1 -1
  51. package/dist/mesh/canonical.js.map +1 -1
  52. package/dist/mesh/client.js.map +1 -1
  53. package/dist/mesh/encoding.js.map +1 -1
  54. package/dist/mesh/self_revoke.js.map +1 -1
  55. package/dist/mesh/siblings.js.map +1 -1
  56. package/dist/mesh/verify.js +1 -1
  57. package/dist/mesh/verify.js.map +1 -1
  58. package/dist/pairing/crypto.js +0 -1
  59. package/dist/pairing/crypto.js.map +1 -1
  60. package/dist/pairing/peer_trust.js.map +1 -1
  61. package/dist/pairing/qr.js.map +1 -1
  62. package/dist/pairing/storage.js +4 -3
  63. package/dist/pairing/storage.js.map +1 -1
  64. package/dist/panel_bridge.js +1 -0
  65. package/dist/panel_bridge.js.map +1 -1
  66. package/dist/paths.d.ts +22 -8
  67. package/dist/paths.js +37 -10
  68. package/dist/paths.js.map +1 -1
  69. package/dist/protocol/codec.js.map +1 -1
  70. package/dist/rooms.js +1 -4
  71. package/dist/rooms.js.map +1 -1
  72. package/dist/session/bridge.js.map +1 -1
  73. package/dist/session/broker.js.map +1 -1
  74. package/dist/session/broker_remote.js.map +1 -1
  75. package/dist/session/capabilities.d.ts +11 -0
  76. package/dist/session/capabilities.js +35 -0
  77. package/dist/session/capabilities.js.map +1 -0
  78. package/dist/session/cwd_lock.js +3 -3
  79. package/dist/session/cwd_lock.js.map +1 -1
  80. package/dist/session/debug_log.js +13 -3
  81. package/dist/session/debug_log.js.map +1 -1
  82. package/dist/session/envelope.js.map +1 -1
  83. package/dist/session/global_config.d.ts +1 -1
  84. package/dist/session/global_config.js +1 -1
  85. package/dist/session/global_config.js.map +1 -1
  86. package/dist/session/ipc.d.ts +2 -2
  87. package/dist/session/ipc.js.map +1 -1
  88. package/dist/session/leader_election.js.map +1 -1
  89. package/dist/session/local_config.d.ts +37 -0
  90. package/dist/session/local_config.js +45 -1
  91. package/dist/session/local_config.js.map +1 -1
  92. package/dist/session/mesh_node.d.ts +1 -1
  93. package/dist/session/mesh_node.js +1 -1
  94. package/dist/session/mesh_node.js.map +1 -1
  95. package/dist/session/peer.js.map +1 -1
  96. package/dist/session/peer_inventory.js.map +1 -1
  97. package/dist/session/peer_limits.js.map +1 -1
  98. package/dist/session/received_images.d.ts +63 -0
  99. package/dist/session/received_images.js +273 -0
  100. package/dist/session/received_images.js.map +1 -0
  101. package/dist/session/relay_lifecycle.d.ts +219 -0
  102. package/dist/session/relay_lifecycle.js +764 -0
  103. package/dist/session/relay_lifecycle.js.map +1 -0
  104. package/dist/session/rpc_envelope.d.ts +10 -0
  105. package/dist/session/rpc_envelope.js +9 -0
  106. package/dist/session/rpc_envelope.js.map +1 -1
  107. package/dist/session/rpc_handlers.d.ts +53 -0
  108. package/dist/session/rpc_handlers.js +268 -0
  109. package/dist/session/rpc_handlers.js.map +1 -0
  110. package/dist/session/rpc_inbound.d.ts +41 -6
  111. package/dist/session/rpc_inbound.js +55 -0
  112. package/dist/session/rpc_inbound.js.map +1 -1
  113. package/dist/session/setup_wizard.js.map +1 -1
  114. package/dist/session/tools.js.map +1 -1
  115. package/dist/session/wizard.js.map +1 -1
  116. package/dist/state_migration.d.ts +45 -0
  117. package/dist/state_migration.js +104 -0
  118. package/dist/state_migration.js.map +1 -0
  119. package/dist/subagent_rooms.d.ts +19 -0
  120. package/dist/subagent_rooms.js +146 -14
  121. package/dist/subagent_rooms.js.map +1 -1
  122. package/dist/test_hooks.d.ts +180 -0
  123. package/dist/test_hooks.js +98 -0
  124. package/dist/test_hooks.js.map +1 -0
  125. package/dist/transport/peer_channel.d.ts +11 -0
  126. package/dist/transport/peer_channel.js +34 -11
  127. package/dist/transport/peer_channel.js.map +1 -1
  128. package/dist/transport/pi_forward_client.js.map +1 -1
  129. package/dist/transport/relay_client.d.ts +5 -2
  130. package/dist/transport/relay_client.js +6 -3
  131. package/dist/transport/relay_client.js.map +1 -1
  132. package/dist/ui/footer.js.map +1 -1
  133. package/docs/daemon.md +160 -170
  134. package/package.json +1 -1
  135. package/service-templates/launchd.plist.template +5 -4
  136. package/service-templates/systemd.service.template +3 -2
@@ -0,0 +1,273 @@
1
+ /**
2
+ * Received-image preview pipeline.
3
+ *
4
+ * Everything the App↔Pi image path needs once an Android `user_message` with
5
+ * images lands on a PlainPeerChannel: decode + persist the payloads under the
6
+ * private image cache, emit the metadata-only TUI preview custom message
7
+ * (deferred while a turn is active), register the TUI renderer for it, and
8
+ * hand the multimodal content to the agent via sendUserMessage.
9
+ *
10
+ * Seam: index.ts (composition root) owns the mutable module state this
11
+ * pipeline reads (`_pi`, `_myRoomMeta`, the root session record) and the
12
+ * `_wakeAgent` helper; they are threaded through `ImagePipelineDeps`. This
13
+ * module MUST NOT import `../index.js` (circular import). The
14
+ * pending-preview buffer is pipeline-local and lives here in module scope.
15
+ *
16
+ * NOT here: `_isPureDataContextMessage` / `_filterInternalMessagesFromContext`
17
+ * — those are GENERIC context filters (issue #105, pure-data events must not
18
+ * reach the model) used by the extension registration far beyond the image
19
+ * path; they stay in index.ts and call `_isReceivedImageContextMessage` below.
20
+ */
21
+ import { join } from "node:path";
22
+ import { chmodSync, readFileSync, writeFileSync } from "node:fs";
23
+ import { Box, Container, Image, Text } from "@earendil-works/pi-tui";
24
+ import { IMAGE_PREVIEW_MIME, _imageCacheRootDir, _imageExtension, _safeFilenameToken, _safePreviewPath, _renderablePngPathFromImage, _decodeImagePayload, } from "../image_codec.js";
25
+ /** Custom-message type for the metadata-only TUI preview (un-bien namespace). */
26
+ const UNBIEN_RECEIVED_IMAGE_TYPE = "un-bien:received-image";
27
+ /** Previews parked while a turn is active; flushed on agent_end / stop. */
28
+ const _pendingReceivedImagePreviews = [];
29
+ export function _sendReceivedImagePreviewNow(deps, details) {
30
+ if (!deps.pi)
31
+ return;
32
+ try {
33
+ deps.pi.sendMessage({
34
+ customType: UNBIEN_RECEIVED_IMAGE_TYPE,
35
+ content: "",
36
+ display: true,
37
+ details,
38
+ });
39
+ }
40
+ catch {
41
+ // TUI preview is best-effort; skip on failure.
42
+ }
43
+ }
44
+ export function _shouldDeferReceivedImagePreview(deps) {
45
+ return deps.rootState().turnId !== null || deps.myRoomMeta?.working === true;
46
+ }
47
+ export function _sendReceivedImagePreview(deps, details, delivery = "immediate") {
48
+ if (delivery === "defer" || _shouldDeferReceivedImagePreview(deps)) {
49
+ _pendingReceivedImagePreviews.push(details);
50
+ return;
51
+ }
52
+ _sendReceivedImagePreviewNow(deps, details);
53
+ }
54
+ export function _flushPendingReceivedImagePreviews(deps) {
55
+ if (_pendingReceivedImagePreviews.length === 0)
56
+ return;
57
+ const pending = _pendingReceivedImagePreviews.splice(0);
58
+ for (const details of pending)
59
+ _sendReceivedImagePreviewNow(deps, details);
60
+ }
61
+ /** Drop parked previews without rendering (teardown path — mirrors `_goIdle`). */
62
+ export function clearPendingReceivedImagePreviews() {
63
+ _pendingReceivedImagePreviews.length = 0;
64
+ }
65
+ export async function _collectReceivedImagePreviews(msg) {
66
+ if (!msg.images || msg.images.length === 0)
67
+ return [];
68
+ const previews = [];
69
+ const text = typeof msg.text === "string" ? msg.text : "";
70
+ const dir = _imageCacheRootDir();
71
+ for (let i = 0; i < msg.images.length; i += 1) {
72
+ const image = msg.images[i];
73
+ const mime = typeof image?.mime === "string" ? image.mime : "unknown";
74
+ if (!image || typeof image.data !== "string") {
75
+ console.error(`[un-bien] malformed image in message ${msg.id} index=${i}`);
76
+ previews.push({
77
+ messageId: msg.id,
78
+ index: i,
79
+ mime,
80
+ ...(text ? { text } : {}),
81
+ error: "malformed image payload",
82
+ reason: "missing mime/data payload fields",
83
+ });
84
+ continue;
85
+ }
86
+ const decoded = _decodeImagePayload(image.data, image.mime);
87
+ if (!decoded.ok) {
88
+ console.error(`[un-bien] skipped image id=${msg.id} index=${i}: ${decoded.reason}`);
89
+ previews.push({
90
+ messageId: msg.id,
91
+ index: i,
92
+ mime: image.mime,
93
+ ...(text ? { text } : {}),
94
+ error: "invalid image payload",
95
+ reason: decoded.reason,
96
+ });
97
+ continue;
98
+ }
99
+ const ext = _imageExtension(image.mime);
100
+ if (!ext) {
101
+ console.error(`[un-bien] unsupported image mime in message ${msg.id} index=${i}: ${image.mime}`);
102
+ previews.push({
103
+ messageId: msg.id,
104
+ index: i,
105
+ mime: image.mime,
106
+ ...(text ? { text } : {}),
107
+ error: "invalid image payload",
108
+ reason: `unsupported mime: ${image.mime}`,
109
+ });
110
+ continue;
111
+ }
112
+ const filename = `${_safeFilenameToken(msg.id)}-${i}.${ext}`;
113
+ const path = join(dir, filename);
114
+ try {
115
+ writeFileSync(path, decoded.decoded, { mode: 0o600 });
116
+ try {
117
+ chmodSync(path, 0o600);
118
+ }
119
+ catch {
120
+ /* best-effort permission hardening */
121
+ }
122
+ const previewPath = image.mime === IMAGE_PREVIEW_MIME
123
+ ? undefined
124
+ : await _renderablePngPathFromImage(image.data, image.mime, _safePreviewPath(dir, msg.id, i));
125
+ previews.push({
126
+ messageId: msg.id,
127
+ index: i,
128
+ mime: image.mime,
129
+ size: decoded.size,
130
+ path,
131
+ ...(previewPath ? { previewPath } : {}),
132
+ ...(text ? { text } : {}),
133
+ });
134
+ }
135
+ catch (error) {
136
+ const detail = error instanceof Error ? error.message : String(error);
137
+ console.error(`[un-bien] failed saving image id=${msg.id} index=${i}: ${detail}`);
138
+ previews.push({
139
+ messageId: msg.id,
140
+ index: i,
141
+ mime: image.mime,
142
+ ...(text ? { text } : {}),
143
+ path,
144
+ error: "failed to save image",
145
+ reason: detail,
146
+ });
147
+ }
148
+ }
149
+ return previews;
150
+ }
151
+ export async function _emitReceivedImagePreviews(deps, msg, delivery = "immediate") {
152
+ const previews = await _collectReceivedImagePreviews(msg);
153
+ for (const preview of previews)
154
+ _sendReceivedImagePreview(deps, preview, delivery);
155
+ }
156
+ export function _registerReceivedImageRenderer(pi) {
157
+ pi.registerMessageRenderer(UNBIEN_RECEIVED_IMAGE_TYPE, (message, _options, theme) => {
158
+ const details = (message.details ?? {});
159
+ const path = typeof details.path === "string" ? details.path : "";
160
+ const previewPath = typeof details.previewPath === "string" ? details.previewPath : "";
161
+ const mime = typeof details.mime === "string"
162
+ ? details.mime
163
+ : "application/octet-stream";
164
+ const inlineImagePath = previewPath.length > 0
165
+ ? previewPath
166
+ : mime === IMAGE_PREVIEW_MIME
167
+ ? path
168
+ : "";
169
+ const size = typeof details.size === "number" ? details.size : undefined;
170
+ const index = typeof details.index === "number" ? details.index : undefined;
171
+ const text = typeof details.text === "string" ? details.text.trim() : "";
172
+ const messageId = typeof details.messageId === "string" ? details.messageId : "unknown";
173
+ const error = typeof details.error === "string" ? details.error : undefined;
174
+ const reason = typeof details.reason === "string" ? details.reason : undefined;
175
+ const label = `📷 Photo from Android (${messageId}${index === undefined ? "" : ` #${index}`})`;
176
+ const lines = [
177
+ theme.fg("customMessageLabel", label),
178
+ theme.fg("customMessageText", `Saved: ${path || "(not saved)"}`),
179
+ ];
180
+ if (size !== undefined)
181
+ lines.push(theme.fg("customMessageText", `Size: ${size} bytes`));
182
+ if (mime)
183
+ lines.push(theme.fg("customMessageText", `MIME: ${mime}`));
184
+ if (error)
185
+ lines.push(theme.fg("customMessageText", `Error: ${error}`));
186
+ if (reason)
187
+ lines.push(theme.fg("customMessageText", `Reason: ${reason}`));
188
+ if (text)
189
+ lines.push(theme.fg("customMessageText", `Text: ${text}`));
190
+ const container = new Container();
191
+ const metadata = new Box(1, 1, (line) => theme.bg("customMessageBg", line));
192
+ metadata.addChild(new Text(lines.join("\n")));
193
+ container.addChild(metadata);
194
+ if (inlineImagePath && !error) {
195
+ try {
196
+ const imageData = readFileSync(inlineImagePath).toString("base64");
197
+ if (imageData.length > 0) {
198
+ const image = new Image(imageData, IMAGE_PREVIEW_MIME, {
199
+ fallbackColor: (str) => theme.fg("customMessageText", str),
200
+ });
201
+ // Keep Kitty image rows out of Box padding/background so pi-tui can
202
+ // preserve the empty reserved rows that make inline images visible.
203
+ container.addChild(image);
204
+ }
205
+ }
206
+ catch {
207
+ // Keep the metadata-only fallback on any IO/terminal issue.
208
+ }
209
+ }
210
+ return container;
211
+ });
212
+ }
213
+ export function _isReceivedImageContextMessage(message) {
214
+ return (typeof message === "object" &&
215
+ message !== null &&
216
+ message.role === "custom" &&
217
+ message.customType ===
218
+ UNBIEN_RECEIVED_IMAGE_TYPE);
219
+ }
220
+ export function _contentFromUserMessage(msg) {
221
+ return msg.images && msg.images.length > 0
222
+ ? [
223
+ ...msg.images.map((img) => ({
224
+ type: "image",
225
+ data: img.data,
226
+ mimeType: img.mime,
227
+ })),
228
+ { type: "text", text: msg.text },
229
+ ]
230
+ : msg.text;
231
+ }
232
+ export async function _deliverImageUserMessage(deps, sender, msg, shouldSteer) {
233
+ const previewDelivery = shouldSteer ||
234
+ deps.rootState().turnId !== null ||
235
+ deps.myRoomMeta?.working === true
236
+ ? "defer"
237
+ : "immediate";
238
+ const emitPreview = async () => {
239
+ try {
240
+ await _emitReceivedImagePreviews(deps, msg, previewDelivery);
241
+ }
242
+ catch (error) {
243
+ const detail = error instanceof Error ? error.message : String(error);
244
+ console.error(`[un-bien] failed emitting image preview id=${msg.id}: ${detail}`);
245
+ }
246
+ };
247
+ if (previewDelivery === "immediate") {
248
+ await emitPreview();
249
+ }
250
+ else {
251
+ void emitPreview().finally(() => {
252
+ if (!_shouldDeferReceivedImagePreview(deps))
253
+ _flushPendingReceivedImagePreviews(deps);
254
+ });
255
+ }
256
+ const previousTurnId = deps.rootState().turnId;
257
+ const seededTurnId = !shouldSteer || deps.rootState().turnId === null;
258
+ if (seededTurnId)
259
+ deps.rootState().turnId = msg.id;
260
+ const wake = deps.wakeAgent(_contentFromUserMessage(msg), `app user_message id=${msg.id} (+${msg.images?.length ?? 0} image)`, "steer");
261
+ if (!wake.ok) {
262
+ if (seededTurnId)
263
+ deps.rootState().turnId = previousTurnId;
264
+ sender.send({
265
+ type: "error",
266
+ code: "internal_error",
267
+ in_reply_to: msg.id,
268
+ message: `Agent rejected incoming message: ${wake.detail}`,
269
+ });
270
+ return;
271
+ }
272
+ }
273
+ //# sourceMappingURL=received_images.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"received_images.js","sourceRoot":"","sources":["../../src/session/received_images.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,219 @@
1
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { Ed25519Keypair } from "../pairing/crypto.js";
3
+ import type { SelfRevoke } from "../mesh/self_revoke.js";
4
+ import type { MeshTopologySnapshot } from "../mesh/siblings.js";
5
+ import type { ClientMessage, ServerMessage, ThinkingLevel } from "../protocol/types.js";
6
+ import { RelayClient } from "../transport/relay_client.js";
7
+ import { PlainPeerChannel } from "../transport/peer_channel.js";
8
+ import type { MeshNode } from "./mesh_node.js";
9
+ import { type EnvelopeMessage } from "./rpc_envelope.js";
10
+ import type { CommandDeps } from "../commands/deps.js";
11
+ /**
12
+ * The seam between index.ts (composition root) and the relay lifecycle +
13
+ * owner-management code.
14
+ *
15
+ * Same shape as `CommandDeps`: index.ts owns every piece of mutable module
16
+ * state this code touches; it reaches it only through accessor closures /
17
+ * function references on this object. Members are added exactly as the moved
18
+ * code requires them — nothing speculative. Mutable state written here is a
19
+ * get/set pair; state only read is `readonly`.
20
+ */
21
+ export interface RelayLifecycleDeps {
22
+ /** Remote state machine: `idle` → `started` (`paired` is derived). */
23
+ state: "idle" | "started";
24
+ /** Live relay WS connection, null when off. */
25
+ relay: RelayClient | null;
26
+ /** URL used by the current relay connection (http(s):// canonical form). */
27
+ relayUrl: string | null;
28
+ /** Shortid of the most recently attached peer (UX hint only). */
29
+ peerShort: string;
30
+ /** Auto-listener dispose fn for the current relay connection. */
31
+ stopAutoListener: (() => void) | null;
32
+ /** SelfRevoke topology producer for the relay path. */
33
+ selfRevoke: SelfRevoke | null;
34
+ /** Epoch guarding the SelfRevoke producer identity. */
35
+ selfRevokeEpoch: number;
36
+ /** Producer epoch that last published a verified topology. */
37
+ selfRevokeTopologyReadyEpoch: number;
38
+ /** Last topology snapshot published by the producer. */
39
+ selfRevokeTopology: MeshTopologySnapshot | null;
40
+ /** Root replacement authority epoch (session replacements). */
41
+ rootLifecycleGeneration: number;
42
+ /** Connected owner channels, keyed by canonical owner pubkey. */
43
+ readonly activePeers: Map<string, PlainPeerChannel>;
44
+ /** Local UDS mesh node, null when not joined. */
45
+ readonly meshNode: MeshNode | null;
46
+ /** Cached Ed25519 identity of this connect cycle. */
47
+ readonly cachedEd25519: Ed25519Keypair | null;
48
+ /** THE App↔Pi room id for the current chat session. */
49
+ readonly myRoomId: string | null;
50
+ /** Room meta hello payload persisted for reconnect replay. */
51
+ readonly myRoomMeta: {
52
+ name: string;
53
+ cwd: string;
54
+ model?: string;
55
+ thinking?: ThinkingLevel;
56
+ working?: boolean;
57
+ sessionId?: string;
58
+ } | null;
59
+ /** Epoch ms the state machine entered 'started'. */
60
+ readonly sessionStartedAt: number | null;
61
+ /** Current session's cwd (sessionManager.getCwd(); process.cwd() fallback). */
62
+ sessionCwd(): string;
63
+ /** Set by session_shutdown; blocks listener authority until rearm. */
64
+ readonly disposed: boolean;
65
+ /** The ROOT session's ExtensionAPI (never a subagent child's). */
66
+ readonly pi: ExtensionAPI | null;
67
+ /** The root session's pi sessionId, null pre-sessionManager. */
68
+ readonly rootSessionId: string | null;
69
+ /** The `/unbien` command seam index.ts builds (`_cmdStart` for relay:on). */
70
+ readonly commandDeps: CommandDeps;
71
+ /** The ROOT session's state record (turn teardown + sessionId reads). */
72
+ rootState(): {
73
+ turnId: string | null;
74
+ sessionManager?: {
75
+ getSessionId(): string;
76
+ } | null;
77
+ };
78
+ /** Test-visible state: `idle` | `started` | derived `paired`. */
79
+ getState(): "idle" | "started" | "paired";
80
+ /** Repaint the footer status line from current state. */
81
+ refreshFooter(ctx?: {
82
+ ui?: {
83
+ setStatus?: unknown;
84
+ setTitle?: unknown;
85
+ };
86
+ } | null): void;
87
+ /** ctx.ui.notify that never throws (stale-ctx safe). */
88
+ safeNotify(message: string, level?: "info" | "warning" | "error", preferred?: {
89
+ ui?: unknown;
90
+ } | null): void;
91
+ /** Reads peers.json and refreshes the global-pairings cache + footer. */
92
+ refreshPairingsCache(): void;
93
+ /** Friendly (configured or derived) agent name for a cwd. */
94
+ displayName(cwd: string): string;
95
+ /** Attach the cross-PC bridge once relay + leader are both up. */
96
+ attachBridgeIfReady(): void;
97
+ /** Stock ClientMessage router (ping / cancel / pre-attach paths). */
98
+ routeClientMessageFrom(sender: PlainPeerChannel, msg: ClientMessage, ctx: Pick<ExtensionContext, "abort">): void;
99
+ /** Envelope-carried pi RpcCommand router. */
100
+ routeRpcCommandFrom(sender: PlainPeerChannel, env: EnvelopeMessage): void;
101
+ /** un-bien plane (session_sync / session_launch) router. */
102
+ routeUnBienPlaneFrom(sender: PlainPeerChannel, env: EnvelopeMessage): void;
103
+ /** Always-fresh ctx (session_start ctx preferred over captured ones). */
104
+ liveCtx(): {
105
+ ui?: unknown;
106
+ } | null;
107
+ /** Fallback ctx when no live ctx exists (notify/abort no-ops). */
108
+ readonly noopCtx: {
109
+ ui: {
110
+ notify(): void;
111
+ };
112
+ abort(): void;
113
+ };
114
+ /** Rename this agent (mesh + relay room) — control `rename:<name>`. */
115
+ renameAgent(newName: string): Promise<void>;
116
+ }
117
+ /** Sentinel prefix for a transparent control message an RPC client sends on the
118
+ * `prompt` channel (stdin). The `input` hook intercepts it, runs the action,
119
+ * and swallows it (`action:"handled"`) so it never becomes an LLM turn or a
120
+ * transcript entry. Starts with NUL so it can't collide with real user input
121
+ * and doesn't begin with "/" (which would route to the command parser). */
122
+ export declare const CTRL_PREFIX = "\0un-bien-ctrl:";
123
+ /** Relay connectivity as seen by an RPC client (Cockpit). Derived from
124
+ * `_state` + `_relay`: "disconnected" = relay off (idle); "connected" = live
125
+ * WS; "reconnecting" = was on, WS dropped, retrying. Surfaced via the
126
+ * `un-bien:relay-state` custom message (see `_emitRelayState`). */
127
+ export type RelayConnectivity = "connected" | "reconnecting" | "disconnected";
128
+ /** Relay-lifecycle generation (module-local). index.ts bumps it on
129
+ * session_shutdown and fronts it through the CommandDeps get/set pair. */
130
+ export declare function _getRelayLifecycleGeneration(): number;
131
+ /** @see _getRelayLifecycleGeneration */
132
+ export declare function _setRelayLifecycleGeneration(value: number): void;
133
+ /** Pending reconnect timer (module-local) — the test-hooks reconnect probe. */
134
+ export declare function _getReconnectTimer(): ReturnType<typeof setTimeout> | null;
135
+ /** Returns true when at least one owner is attached. Derived `paired` UX. */
136
+ export declare function _anyPeerActive(deps: RelayLifecycleDeps): boolean;
137
+ /** Broadcast for the extension_ui bridge. The bridge only ever emits
138
+ * `extension_ui_request`, sent ENVELOPE-ONLY as a `{rpc}` frame (the wire
139
+ * shape mirrors the SDK rpc contract 1:1). No stock fallback. */
140
+ export declare function _uiBroadcast(deps: RelayLifecycleDeps, msg: ServerMessage): void;
141
+ /** Broadcast for the panel bridge. The bridge only ever emits `panel_update`,
142
+ * forwarded ENVELOPE-ONLY as `{evt:{channel:"panel", data}}` (the {evt} plane);
143
+ * the app folds it into its panel store. No stock fallback. */
144
+ export declare function _panelBroadcast(deps: RelayLifecycleDeps, msg: ServerMessage): void;
145
+ /** Fan an rpc-envelope frame out to every attached peer (base64 ct via each
146
+ * channel) — the single owner-fanout path for `{ rpc | evt }` messages. */
147
+ export declare function _broadcastEnvelope(deps: RelayLifecycleDeps, env: EnvelopeMessage): void;
148
+ /**
149
+ * Teardown-path variant of `_broadcastEnvelope`: awaits each channel's ws
150
+ * send-completion (frame handed to the socket) — bounded by `timeoutMs` so a
151
+ * wedged socket can never hang a shutdown. The `session_shutdown` broadcast
152
+ * MUST use this: its handler tears the relay down immediately after, and a
153
+ * fire-and-forget send only beats `close()` by incidental FIFO luck.
154
+ */
155
+ export declare function _broadcastEnvelopeFlushed(deps: RelayLifecycleDeps, env: EnvelopeMessage, timeoutMs?: number): Promise<void>;
156
+ /** Detaches a single owner's channel + removes it from the map. Used by
157
+ * `_onPeerDisconnect`, `_cmdRevoke`, and the SelfRevoke callback. */
158
+ export declare function _detachPeerChannel(deps: RelayLifecycleDeps, appPeerId: string): void;
159
+ /**
160
+ * Full teardown: stop listener, detach channel, close relay → idle.
161
+ */
162
+ export declare function _goIdle(deps: RelayLifecycleDeps): void;
163
+ /**
164
+ * Called when the relay WS closes unexpectedly (network drop, relay restart,
165
+ * etc.). Does a **partial** teardown — keeps `_sessionStartedAt`, `_messageBuffer`,
166
+ * `_relayUrl`, `_cachedEd25519`, `_peerShort` so the session can resume on
167
+ * reconnect — and schedules an `_attemptReconnect`.
168
+ *
169
+ * Peer (app) reconnect after a successful relay reconnect is handled by the
170
+ * existing auto-listener via `peers.json` lookup, so we don't need to track
171
+ * the prior peer here; we just go back to `started` and wait.
172
+ */
173
+ export declare function _onRelayClose(deps: RelayLifecycleDeps, closedRelay: RelayClient): void;
174
+ /** Current relay connectivity, derived from `_state` + `_relay`. */
175
+ export declare function _relayStatus(deps: RelayLifecycleDeps): RelayConnectivity;
176
+ /**
177
+ * Emit the `un-bien:relay-state` custom message so an RPC client (Cockpit)
178
+ * can render a relay on/off indicator. Pure data (`display:false`) — never
179
+ * shown in the transcript. De-duped on the connectivity value; pass
180
+ * `force=true` to answer an explicit `relay:status` query regardless.
181
+ */
182
+ export declare function _emitRelayState(deps: RelayLifecycleDeps, force?: boolean): void;
183
+ /** Minimal ctx for relay start/stop driven by a control message (no command
184
+ * ctx is available in the `input` hook). cwd matches the daemon's launch dir,
185
+ * so the derived relay room is identical to the one `_cmdStart` first used. */
186
+ export declare function _controlCtx(): Pick<ExtensionContext, "ui" | "cwd">;
187
+ /**
188
+ * `ui.notify` for headless contexts (daemon auto-init + control channel). There
189
+ * is no TUI, and the RPC client (Cockpit) already gets everything it needs via
190
+ * structured events (`un-bien:relay-state`, `un-bien:name-assigned`,
191
+ * room_meta) — so routine INFO chatter would just pollute the client's captured
192
+ * stderr. We drop info and forward only warnings/errors (kept for the
193
+ * supervisor's journal / genuine failures). The interactive Pi keeps its normal
194
+ * footer/notify path — this only affects headless ctxs.
195
+ */
196
+ export declare function _headlessUi(): {
197
+ notify: (msg: string, type?: "info" | "warning" | "error") => void;
198
+ };
199
+ /**
200
+ * Handle a transparent control command from an RPC client (Cockpit), received
201
+ * as a `CTRL_PREFIX`-tagged input the `input` hook swallowed. Toggles the relay
202
+ * WITHOUT leaving the local mesh (relay-only: `_cmdStart` up / `_goIdle` down),
203
+ * then emits the fresh state. `relay:status` just re-emits (no change) so the
204
+ * client can sync its button after (re)attaching to the RPC stream.
205
+ */
206
+ export declare function _handleControl(deps: RelayLifecycleDeps, cmd: string): Promise<void>;
207
+ /**
208
+ * Per-owner disconnect callback. Fires when one specific owner's channel
209
+ * detaches (e.g. relay told us the peer is gone). Other owners' channels
210
+ * keep running — relay stays "started".
211
+ *
212
+ * Exported so tests can trigger the disconnect path for a specific peer.
213
+ *
214
+ * Backward-compat: a no-arg call (legacy tests / pre-W2D callers) falls
215
+ * back to detaching the most recently attached peer, mirroring the old
216
+ * singleton semantics.
217
+ */
218
+ export declare function _onPeerDisconnect(deps: RelayLifecycleDeps, appPeerId?: string): void;
219
+ export declare function _installAutoListener(deps: RelayLifecycleDeps, relay: RelayClient): () => void;