@geohar/un-bien 0.7.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 (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +726 -0
  3. package/dist/actions/handlers.d.ts +77 -0
  4. package/dist/actions/handlers.js +26 -0
  5. package/dist/actions/handlers.js.map +1 -0
  6. package/dist/actions/registry.d.ts +39 -0
  7. package/dist/actions/registry.js +62 -0
  8. package/dist/actions/registry.js.map +1 -0
  9. package/dist/bin/supervisord.d.ts +2 -0
  10. package/dist/bin/supervisord.js +86 -0
  11. package/dist/bin/supervisord.js.map +1 -0
  12. package/dist/config.d.ts +84 -0
  13. package/dist/config.js +115 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/daemon/client.d.ts +20 -0
  16. package/dist/daemon/client.js +131 -0
  17. package/dist/daemon/client.js.map +1 -0
  18. package/dist/daemon/control_protocol.d.ts +176 -0
  19. package/dist/daemon/control_protocol.js +63 -0
  20. package/dist/daemon/control_protocol.js.map +1 -0
  21. package/dist/daemon/cron_log.d.ts +45 -0
  22. package/dist/daemon/cron_log.js +71 -0
  23. package/dist/daemon/cron_log.js.map +1 -0
  24. package/dist/daemon/cron_registry.d.ts +80 -0
  25. package/dist/daemon/cron_registry.js +198 -0
  26. package/dist/daemon/cron_registry.js.map +1 -0
  27. package/dist/daemon/id.d.ts +24 -0
  28. package/dist/daemon/id.js +36 -0
  29. package/dist/daemon/id.js.map +1 -0
  30. package/dist/daemon/install.d.ts +170 -0
  31. package/dist/daemon/install.js +603 -0
  32. package/dist/daemon/install.js.map +1 -0
  33. package/dist/daemon/registry.d.ts +63 -0
  34. package/dist/daemon/registry.js +153 -0
  35. package/dist/daemon/registry.js.map +1 -0
  36. package/dist/daemon/rpc_child.d.ts +194 -0
  37. package/dist/daemon/rpc_child.js +419 -0
  38. package/dist/daemon/rpc_child.js.map +1 -0
  39. package/dist/daemon/supervisor.d.ts +93 -0
  40. package/dist/daemon/supervisor.js +666 -0
  41. package/dist/daemon/supervisor.js.map +1 -0
  42. package/dist/extension_ui_bridge.d.ts +26 -0
  43. package/dist/extension_ui_bridge.js +341 -0
  44. package/dist/extension_ui_bridge.js.map +1 -0
  45. package/dist/index.d.ts +197 -0
  46. package/dist/index.js +5750 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/mcp/mesh_result.d.ts +10 -0
  49. package/dist/mcp/mesh_result.js +42 -0
  50. package/dist/mcp/mesh_result.js.map +1 -0
  51. package/dist/mcp/mesh_server.d.ts +16 -0
  52. package/dist/mcp/mesh_server.js +373 -0
  53. package/dist/mcp/mesh_server.js.map +1 -0
  54. package/dist/mesh/canonical.d.ts +30 -0
  55. package/dist/mesh/canonical.js +63 -0
  56. package/dist/mesh/canonical.js.map +1 -0
  57. package/dist/mesh/client.d.ts +18 -0
  58. package/dist/mesh/client.js +127 -0
  59. package/dist/mesh/client.js.map +1 -0
  60. package/dist/mesh/encoding.d.ts +38 -0
  61. package/dist/mesh/encoding.js +217 -0
  62. package/dist/mesh/encoding.js.map +1 -0
  63. package/dist/mesh/self_revoke.d.ts +73 -0
  64. package/dist/mesh/self_revoke.js +407 -0
  65. package/dist/mesh/self_revoke.js.map +1 -0
  66. package/dist/mesh/siblings.d.ts +49 -0
  67. package/dist/mesh/siblings.js +159 -0
  68. package/dist/mesh/siblings.js.map +1 -0
  69. package/dist/mesh/types.d.ts +34 -0
  70. package/dist/mesh/types.js +11 -0
  71. package/dist/mesh/types.js.map +1 -0
  72. package/dist/mesh/verify.d.ts +17 -0
  73. package/dist/mesh/verify.js +86 -0
  74. package/dist/mesh/verify.js.map +1 -0
  75. package/dist/pairing/crypto.d.ts +8 -0
  76. package/dist/pairing/crypto.js +22 -0
  77. package/dist/pairing/crypto.js.map +1 -0
  78. package/dist/pairing/qr.d.ts +59 -0
  79. package/dist/pairing/qr.js +127 -0
  80. package/dist/pairing/qr.js.map +1 -0
  81. package/dist/pairing/storage.d.ts +120 -0
  82. package/dist/pairing/storage.js +614 -0
  83. package/dist/pairing/storage.js.map +1 -0
  84. package/dist/panel_bridge.d.ts +15 -0
  85. package/dist/panel_bridge.js +409 -0
  86. package/dist/panel_bridge.js.map +1 -0
  87. package/dist/paths.d.ts +39 -0
  88. package/dist/paths.js +51 -0
  89. package/dist/paths.js.map +1 -0
  90. package/dist/protocol/codec.d.ts +7 -0
  91. package/dist/protocol/codec.js +48 -0
  92. package/dist/protocol/codec.js.map +1 -0
  93. package/dist/protocol/types.d.ts +404 -0
  94. package/dist/protocol/types.js +2 -0
  95. package/dist/protocol/types.js.map +1 -0
  96. package/dist/rooms.d.ts +42 -0
  97. package/dist/rooms.js +78 -0
  98. package/dist/rooms.js.map +1 -0
  99. package/dist/session/bridge.d.ts +37 -0
  100. package/dist/session/bridge.js +134 -0
  101. package/dist/session/bridge.js.map +1 -0
  102. package/dist/session/broker.d.ts +189 -0
  103. package/dist/session/broker.js +527 -0
  104. package/dist/session/broker.js.map +1 -0
  105. package/dist/session/broker_remote.d.ts +74 -0
  106. package/dist/session/broker_remote.js +581 -0
  107. package/dist/session/broker_remote.js.map +1 -0
  108. package/dist/session/cwd_lock.d.ts +33 -0
  109. package/dist/session/cwd_lock.js +127 -0
  110. package/dist/session/cwd_lock.js.map +1 -0
  111. package/dist/session/debug_log.d.ts +3 -0
  112. package/dist/session/debug_log.js +32 -0
  113. package/dist/session/debug_log.js.map +1 -0
  114. package/dist/session/envelope.d.ts +33 -0
  115. package/dist/session/envelope.js +117 -0
  116. package/dist/session/envelope.js.map +1 -0
  117. package/dist/session/global_config.d.ts +33 -0
  118. package/dist/session/global_config.js +73 -0
  119. package/dist/session/global_config.js.map +1 -0
  120. package/dist/session/ipc.d.ts +27 -0
  121. package/dist/session/ipc.js +22 -0
  122. package/dist/session/ipc.js.map +1 -0
  123. package/dist/session/leader_election.d.ts +32 -0
  124. package/dist/session/leader_election.js +108 -0
  125. package/dist/session/leader_election.js.map +1 -0
  126. package/dist/session/local_config.d.ts +67 -0
  127. package/dist/session/local_config.js +193 -0
  128. package/dist/session/local_config.js.map +1 -0
  129. package/dist/session/mesh_node.d.ts +116 -0
  130. package/dist/session/mesh_node.js +596 -0
  131. package/dist/session/mesh_node.js.map +1 -0
  132. package/dist/session/peer.d.ts +139 -0
  133. package/dist/session/peer.js +474 -0
  134. package/dist/session/peer.js.map +1 -0
  135. package/dist/session/peer_inventory.d.ts +13 -0
  136. package/dist/session/peer_inventory.js +48 -0
  137. package/dist/session/peer_inventory.js.map +1 -0
  138. package/dist/session/peer_limits.d.ts +9 -0
  139. package/dist/session/peer_limits.js +24 -0
  140. package/dist/session/peer_limits.js.map +1 -0
  141. package/dist/session/rpc_envelope.d.ts +108 -0
  142. package/dist/session/rpc_envelope.js +221 -0
  143. package/dist/session/rpc_envelope.js.map +1 -0
  144. package/dist/session/rpc_inbound.d.ts +55 -0
  145. package/dist/session/rpc_inbound.js +136 -0
  146. package/dist/session/rpc_inbound.js.map +1 -0
  147. package/dist/session/setup_wizard.d.ts +39 -0
  148. package/dist/session/setup_wizard.js +63 -0
  149. package/dist/session/setup_wizard.js.map +1 -0
  150. package/dist/session/tools.d.ts +24 -0
  151. package/dist/session/tools.js +272 -0
  152. package/dist/session/tools.js.map +1 -0
  153. package/dist/session/wizard.d.ts +13 -0
  154. package/dist/session/wizard.js +20 -0
  155. package/dist/session/wizard.js.map +1 -0
  156. package/dist/transport/peer_channel.d.ts +52 -0
  157. package/dist/transport/peer_channel.js +145 -0
  158. package/dist/transport/peer_channel.js.map +1 -0
  159. package/dist/transport/pi_forward_client.d.ts +29 -0
  160. package/dist/transport/pi_forward_client.js +68 -0
  161. package/dist/transport/pi_forward_client.js.map +1 -0
  162. package/dist/transport/relay_client.d.ts +96 -0
  163. package/dist/transport/relay_client.js +210 -0
  164. package/dist/transport/relay_client.js.map +1 -0
  165. package/dist/ui/footer.d.ts +32 -0
  166. package/dist/ui/footer.js +34 -0
  167. package/dist/ui/footer.js.map +1 -0
  168. package/docs/daemon.md +289 -0
  169. package/package.json +92 -0
  170. package/service-templates/launchd.plist.template +35 -0
  171. package/service-templates/systemd.service.template +19 -0
  172. package/service-templates/task-launcher.vbs.template +10 -0
  173. package/service-templates/task-scheduler.xml.template +38 -0
  174. package/skills/agent-network/SKILL.md +310 -0
@@ -0,0 +1,404 @@
1
+ export type PairErrorCode = "token_expired" | "token_consumed" | "token_unknown" | "internal_error";
2
+ export type StreamingBehavior = "steer";
3
+ export type QueuedMessageItem = {
4
+ id: string;
5
+ text: string;
6
+ editable: boolean;
7
+ created_at: number;
8
+ };
9
+ export type ExtensionUiMethod = "select" | "confirm" | "input" | "editor" | "notify";
10
+ export type AskQuestionWireType = "single" | "multi" | "preview";
11
+ export interface AskOptionWire {
12
+ value: string;
13
+ label: string;
14
+ description?: string;
15
+ /** Preview-pane content (preview questions only). */
16
+ preview?: string;
17
+ /** pi-ask addition: option allows freeform custom entry. */
18
+ freeform?: boolean;
19
+ }
20
+ export interface AskQuestionWire {
21
+ id: string;
22
+ label: string;
23
+ prompt: string;
24
+ type: AskQuestionWireType;
25
+ required: boolean;
26
+ /** pi-ask addition: type actually presented after live toggle / policy. */
27
+ presentedType?: AskQuestionWireType;
28
+ /** pi-ask addition: type originally requested by the model. */
29
+ requestedType?: AskQuestionWireType;
30
+ options: AskOptionWire[];
31
+ }
32
+ /** Optional pi-ask enrichment on an extension_ui_request — lets the app render
33
+ * the full flow (multi/preview/notes) instead of the degraded SDK select. A
34
+ * flow maps to ONE request carrying every question; the app renders a
35
+ * full-screen modal and submits ONE response with all answers (pi-ask resolves
36
+ * a flow in a single submit). When `ask` is absent the SDK method/options
37
+ * drive rendering (future generic prompts). */
38
+ export interface AskEnrichmentWire {
39
+ flow_id: string;
40
+ tool_call_id: string | null;
41
+ /** pi-ask RemoteAskSource: "tool" | "answer" | "answer:again" | "ask:replay". */
42
+ source: string;
43
+ title: string | null;
44
+ questions: AskQuestionWire[];
45
+ }
46
+ /** pi-ask RemoteAskAnswer — one question's answered parts.
47
+ *
48
+ * CASING EXCEPTION: inside the `ask` envelope the keys mirror pi-ask's own
49
+ * schema VERBATIM (camelCase: `presentedType`, `requestedType`, `customText`,
50
+ * `optionNotes`) so the bridge can forward the response to pi-ask's submit
51
+ * event without a remap pass. The snake_case convention of this protocol
52
+ * applies at the frame level (`flow_id`, `tool_call_id`, `notify_type`). */
53
+ export interface AskAnswerWire {
54
+ values?: string[];
55
+ customText?: string;
56
+ note?: string;
57
+ optionNotes?: Record<string, string>;
58
+ }
59
+ /** Optional pi-ask enrichment on an extension_ui_response — carries the
60
+ * structured answer so multi/preview/notes survive the round-trip. */
61
+ export type AskResponseEnrichmentWire = {
62
+ flow_id: string;
63
+ kind: "answer";
64
+ mode?: "submit" | "elaborate";
65
+ answers: Record<string, AskAnswerWire>;
66
+ } | {
67
+ flow_id: string;
68
+ kind: "cancel";
69
+ };
70
+ /** ServerMessage: interactive extension prompt. Mirrors RpcExtensionUIRequest
71
+ * (select/confirm/input/editor/notify). The `ask` envelope is present when the
72
+ * prompt originates from a pi-ask flow, carrying the full question schema. */
73
+ export type ExtensionUiRequestWire = {
74
+ type: "extension_ui_request";
75
+ id: string;
76
+ method: "select";
77
+ title: string;
78
+ options: string[];
79
+ ask?: AskEnrichmentWire;
80
+ } | {
81
+ type: "extension_ui_request";
82
+ id: string;
83
+ method: "confirm";
84
+ title: string;
85
+ message: string;
86
+ ask?: AskEnrichmentWire;
87
+ } | {
88
+ type: "extension_ui_request";
89
+ id: string;
90
+ method: "input";
91
+ title: string;
92
+ placeholder?: string;
93
+ ask?: AskEnrichmentWire;
94
+ } | {
95
+ type: "extension_ui_request";
96
+ id: string;
97
+ method: "editor";
98
+ title: string;
99
+ prefill?: string;
100
+ ask?: AskEnrichmentWire;
101
+ } | {
102
+ type: "extension_ui_request";
103
+ id: string;
104
+ method: "notify";
105
+ message: string;
106
+ notify_type?: "info" | "warning" | "error";
107
+ };
108
+ /** ClientMessage: response to an extension_ui_request. Mirrors
109
+ * RpcExtensionUIResponse (value / confirmed / cancelled). The `ask` envelope
110
+ * carries pi-ask's structured answer when the app rendered the rich flow. */
111
+ export type ExtensionUiResponseWire = {
112
+ type: "extension_ui_response";
113
+ id: string;
114
+ value: string;
115
+ ask?: AskResponseEnrichmentWire;
116
+ } | {
117
+ type: "extension_ui_response";
118
+ id: string;
119
+ confirmed: boolean;
120
+ ask?: AskResponseEnrichmentWire;
121
+ } | {
122
+ type: "extension_ui_response";
123
+ id: string;
124
+ cancelled: true;
125
+ ask?: AskResponseEnrichmentWire;
126
+ } | {
127
+ type: "extension_ui_response";
128
+ id: string;
129
+ ask: AskResponseEnrichmentWire;
130
+ };
131
+ export type ClientMessage = {
132
+ type: "pair_request";
133
+ id: string;
134
+ token: string;
135
+ device_name: string;
136
+ } | {
137
+ type: "user_message";
138
+ id: string;
139
+ text: string;
140
+ images?: WireImage[];
141
+ streaming_behavior?: StreamingBehavior;
142
+ } | {
143
+ type: "queued_message_set";
144
+ id: string;
145
+ text: string;
146
+ } | {
147
+ type: "queued_message_clear";
148
+ id: string;
149
+ target_id?: string;
150
+ } | {
151
+ type: "approve_tool";
152
+ id: string;
153
+ tool_call_id: string;
154
+ decision: "allow" | "deny";
155
+ } | {
156
+ type: "cancel";
157
+ id: string;
158
+ target_id: string;
159
+ } | {
160
+ type: "ping";
161
+ id: string;
162
+ } | {
163
+ type: "session_sync";
164
+ id: string;
165
+ limit?: number;
166
+ } | {
167
+ type: "session_new";
168
+ id: string;
169
+ } | {
170
+ type: "session_compact";
171
+ id: string;
172
+ } | {
173
+ type: "model_set";
174
+ id: string;
175
+ provider: string;
176
+ model_id: string;
177
+ } | {
178
+ type: "thinking_set";
179
+ id: string;
180
+ level: ThinkingLevel;
181
+ } | {
182
+ type: "list_models";
183
+ id: string;
184
+ } | {
185
+ type: "session_launch";
186
+ id: string;
187
+ mode: "tmux" | "rpc";
188
+ cwd?: string;
189
+ name?: string;
190
+ } | ExtensionUiResponseWire;
191
+ /**
192
+ * Plan/30 — one inline image attachment on a `user_message`. Mirrors the
193
+ * SDK's `ImageContent` ({@link https }) split across the wire: `data` is the
194
+ * base64-encoded (compressed) image bytes, `mime` its content type
195
+ * (e.g. `"image/jpeg"`). The Pi maps `{ data, mime }` → the SDK's
196
+ * `{ type:"image", data, mimeType }` before handing it to the model.
197
+ */
198
+ export interface WireImage {
199
+ /** Base64-encoded image bytes (compressed app-side). */
200
+ data: string;
201
+ /** MIME type, e.g. `"image/jpeg"`. Maps to the SDK's `mimeType`. */
202
+ mime: string;
203
+ }
204
+ export type Usage = {
205
+ input_tokens: number;
206
+ output_tokens: number;
207
+ };
208
+ export type KnownErrorCode = "tool_approval_required" | "invalid_message" | "unsupported_type" | "too_large" | "rate_limited" | "timeout" | "internal_error";
209
+ export type ErrorCode = KnownErrorCode | (string & {});
210
+ export type SessionHistoryEvent = {
211
+ ts: number;
212
+ type: "user_input";
213
+ id: string;
214
+ text: string;
215
+ images?: WireImage[];
216
+ } | {
217
+ ts: number;
218
+ type: "tool_request";
219
+ tool_call_id: string;
220
+ tool: string;
221
+ args: Record<string, unknown>;
222
+ } | {
223
+ ts: number;
224
+ type: "tool_result";
225
+ tool_call_id: string;
226
+ result?: unknown;
227
+ error?: string;
228
+ images?: WireImage[];
229
+ } | {
230
+ ts: number;
231
+ type: "agent_message";
232
+ in_reply_to: string;
233
+ text: string;
234
+ usage?: Usage;
235
+ images?: WireImage[];
236
+ } | {
237
+ ts: number;
238
+ type: "compaction";
239
+ summary: string;
240
+ tokens_before: number;
241
+ };
242
+ export type ServerMessage = {
243
+ type: "pair_ok";
244
+ in_reply_to: string;
245
+ session_name: string;
246
+ session_started_at: number;
247
+ room_id: string;
248
+ /**
249
+ * Plan/27 Wave A: identifies the host coding agent driving this
250
+ * pi-extension instance. `name` is hardcoded to "Pi coding agent"
251
+ * today; future Pi forks (Claude Code, OpenCode) populate their own
252
+ * here. `version` is the pi-extension `package.json` version.
253
+ * Optional in the wire schema so app-side parsing tolerates older
254
+ * Pi builds that predate this field — every new pairing emits both.
255
+ */
256
+ harness?: {
257
+ name: string;
258
+ version: string;
259
+ };
260
+ /**
261
+ * Plan/27 Wave A: `os.hostname()` of the machine the Pi runs on.
262
+ * App displays it in the device list so the user can distinguish
263
+ * two paired PCs that happen to share a nickname or sit in the
264
+ * same project folder.
265
+ */
266
+ hostname?: string;
267
+ protocol_version?: number;
268
+ capabilities?: string[];
269
+ } | {
270
+ type: "pair_error";
271
+ in_reply_to: string;
272
+ code: PairErrorCode;
273
+ message: string;
274
+ } | {
275
+ type: "user_input";
276
+ id: string;
277
+ text: string;
278
+ streaming_behavior?: StreamingBehavior;
279
+ } | {
280
+ type: "user_message";
281
+ id: string;
282
+ text: string;
283
+ images?: WireImage[];
284
+ streaming_behavior?: StreamingBehavior;
285
+ } | {
286
+ type: "queued_message_state";
287
+ id?: string;
288
+ text?: string;
289
+ items?: QueuedMessageItem[];
290
+ } | {
291
+ type: "steer_consumed";
292
+ id: string;
293
+ } | {
294
+ type: "agent_chunk";
295
+ in_reply_to: string;
296
+ delta: string;
297
+ } | {
298
+ type: "agent_reasoning";
299
+ in_reply_to: string;
300
+ delta: string;
301
+ } | {
302
+ type: "agent_done";
303
+ in_reply_to: string;
304
+ usage?: Usage;
305
+ } | {
306
+ type: "agent_message";
307
+ in_reply_to: string;
308
+ text: string;
309
+ usage?: Usage;
310
+ images?: WireImage[];
311
+ } | {
312
+ type: "compaction";
313
+ summary: string;
314
+ tokens_before: number;
315
+ ts?: number;
316
+ } | {
317
+ type: "tool_request";
318
+ tool_call_id: string;
319
+ tool: string;
320
+ args: Record<string, unknown>;
321
+ } | {
322
+ type: "tool_result";
323
+ tool_call_id: string;
324
+ result?: unknown;
325
+ error?: string;
326
+ images?: WireImage[];
327
+ } | {
328
+ type: "error";
329
+ in_reply_to?: string;
330
+ code: ErrorCode;
331
+ message: string;
332
+ } | {
333
+ type: "cancelled";
334
+ in_reply_to: string;
335
+ target_id: string;
336
+ } | {
337
+ type: "pong";
338
+ in_reply_to: string;
339
+ } | {
340
+ type: "action_ok";
341
+ in_reply_to: string;
342
+ action: ActionName;
343
+ } | {
344
+ type: "action_error";
345
+ in_reply_to: string;
346
+ action: ActionName;
347
+ error: string;
348
+ } | {
349
+ type: "models_list";
350
+ in_reply_to: string;
351
+ models: WireModel[];
352
+ current?: WireModel;
353
+ } | {
354
+ type: "panel_update";
355
+ key: string;
356
+ title: string;
357
+ icon?: string;
358
+ data: unknown;
359
+ } | ExtensionUiRequestWire;
360
+ /**
361
+ * Plan/28 — Stable names for the typed actions the app can request. Kept
362
+ * as a closed string union so a switch in either side gets exhaustiveness
363
+ * checking from the compiler.
364
+ */
365
+ export type ActionName = "session_new" | "session_compact" | "model_set" | "thinking_set" | "session_launch";
366
+ /**
367
+ * Plan/28 — Mirror of the SDK's `ThinkingLevel` (defined in
368
+ * `@earendil-works/pi-agent-core/types`). Re-declared locally so the wire
369
+ * protocol owns its own enum and we don't leak SDK-internal types onto
370
+ * the app's network surface.
371
+ *
372
+ * Note: `"xhigh"` is only honored by select model families — the SDK uses
373
+ * each `Model.thinkingLevelMap` to decide if the requested level is
374
+ * supported, falling back to a sensible neighbour when not. The app
375
+ * surfaces all 6 buttons but can grey out unsupported ones using the
376
+ * model's metadata if the picker fetches it later.
377
+ */
378
+ export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
379
+ /**
380
+ * Plan/28 — Wire shape for one model entry in the app's model picker.
381
+ *
382
+ * Subset of the SDK's `Model<Api>` interface — only the fields the app
383
+ * actually renders. Cost / max-tokens / API class are left off the wire
384
+ * deliberately; if the app's picker grows to need them, they get added
385
+ * here and to the handler's mapping in `index.ts` in one diff.
386
+ */
387
+ export interface WireModel {
388
+ /** Stable identifier inside the provider's catalog. E.g. `"claude-opus-4-7"`. */
389
+ id: string;
390
+ /** Display name for the picker row. E.g. `"Claude Opus 4.7"`. */
391
+ name: string;
392
+ /** Provider slug. E.g. `"anthropic"`, `"openai"`. */
393
+ provider: string;
394
+ /** Whether the model supports the thinking surface (`reasoning: true`
395
+ * in the SDK). Useful so the app can decide whether the thinking
396
+ * segmented control should be enabled when this model is selected. */
397
+ reasoning: boolean;
398
+ /** Context window in tokens, for display in the picker subtitle. */
399
+ context_window: number;
400
+ /** Plan/30: true when the model accepts image input (SDK `Model.input`
401
+ * includes `"image"`). The app uses it to enable/disable the attach
402
+ * button — a text-only model greys out image attachments. */
403
+ vision: boolean;
404
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/protocol/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Deterministic room id derived from a cwd. Two Pi processes in the same
3
+ * directory produce the same id; different cwds produce different ids
4
+ * (with cryptographic-strength collision resistance). Symlinks are resolved
5
+ * via `realpath` so `/a` and `/symlink-to-a` map to the same room.
6
+ *
7
+ * Format: first 12 chars of base64url(sha256(realpath)).
8
+ */
9
+ export declare function roomIdForCwd(cwd: string): string;
10
+ /**
11
+ * THE App<->Pi `room_id` basis: derived from the stable pi SESSION ID (which is
12
+ * durable across resume — it lives in the session file header and is reused when
13
+ * the file is reopened; a fresh session gets a new id). Keying the room on the
14
+ * session id makes it (a) stable across relay reconnects, so proactive frames
15
+ * can't drift onto a different room than the session announces, and (b) unique
16
+ * per chat session, so two same-NAME chats in one folder are distinct. The
17
+ * display label stays the session's title/name; this is the routing key only.
18
+ *
19
+ * 12-char `base64url(sha256(sessionId))` — same shape as `roomIdForCwd`, and it
20
+ * keeps the raw session id off the wire.
21
+ */
22
+ export declare function roomIdForSession(sessionId: string): string;
23
+ /**
24
+ * THE single derivation of the App↔Pi `room_id` (plan/41) — keyed by
25
+ * `(cwd, name)` so several agents in the SAME folder get distinct rooms (the
26
+ * app then renders one tile per agent instead of merging them into one).
27
+ *
28
+ * Default-preserving: when `name` is absent OR equals `defaultAgentName(cwd)`
29
+ * (an agent with no custom `agent_name`), it returns the LEGACY `roomIdForCwd`
30
+ * EXACTLY — so a single unnamed agent's existing conversation is NOT re-keyed
31
+ * on upgrade. A custom or `#N`-suffixed name → a name-scoped id (same formula
32
+ * the cwd-lock uses).
33
+ *
34
+ * Using the ASSIGNED leaf name (the broker's `#N` on collision) disambiguates
35
+ * even two unnamed agents: the 1st stays `folder` (== default → legacy room),
36
+ * the 2nd becomes `folder#2` (≠ default → name-scoped room).
37
+ *
38
+ * INVARIANT: every callsite that derives the App↔Pi room for the same agent
39
+ * MUST go through this function — otherwise the app would pair on a room the
40
+ * Pi never announces.
41
+ */
42
+ export declare function roomIdFor(cwd: string, name?: string): string;
package/dist/rooms.js ADDED
@@ -0,0 +1,78 @@
1
+ import { createHash } from "node:crypto";
2
+ import { realpathSync } from "node:fs";
3
+ import { defaultAgentName } from "./session/local_config.js";
4
+ /**
5
+ * Deterministic room id derived from a cwd. Two Pi processes in the same
6
+ * directory produce the same id; different cwds produce different ids
7
+ * (with cryptographic-strength collision resistance). Symlinks are resolved
8
+ * via `realpath` so `/a` and `/symlink-to-a` map to the same room.
9
+ *
10
+ * Format: first 12 chars of base64url(sha256(realpath)).
11
+ */
12
+ export function roomIdForCwd(cwd) {
13
+ let target;
14
+ try {
15
+ target = realpathSync(cwd);
16
+ }
17
+ catch {
18
+ // cwd doesn't exist (unlikely in production) — fallback to raw path.
19
+ target = cwd;
20
+ }
21
+ return createHash("sha256").update(target).digest("base64url").slice(0, 12);
22
+ }
23
+ /**
24
+ * THE App<->Pi `room_id` basis: derived from the stable pi SESSION ID (which is
25
+ * durable across resume — it lives in the session file header and is reused when
26
+ * the file is reopened; a fresh session gets a new id). Keying the room on the
27
+ * session id makes it (a) stable across relay reconnects, so proactive frames
28
+ * can't drift onto a different room than the session announces, and (b) unique
29
+ * per chat session, so two same-NAME chats in one folder are distinct. The
30
+ * display label stays the session's title/name; this is the routing key only.
31
+ *
32
+ * 12-char `base64url(sha256(sessionId))` — same shape as `roomIdForCwd`, and it
33
+ * keeps the raw session id off the wire.
34
+ */
35
+ export function roomIdForSession(sessionId) {
36
+ return createHash("sha256")
37
+ .update(sessionId)
38
+ .digest("base64url")
39
+ .slice(0, 12);
40
+ }
41
+ /**
42
+ * THE single derivation of the App↔Pi `room_id` (plan/41) — keyed by
43
+ * `(cwd, name)` so several agents in the SAME folder get distinct rooms (the
44
+ * app then renders one tile per agent instead of merging them into one).
45
+ *
46
+ * Default-preserving: when `name` is absent OR equals `defaultAgentName(cwd)`
47
+ * (an agent with no custom `agent_name`), it returns the LEGACY `roomIdForCwd`
48
+ * EXACTLY — so a single unnamed agent's existing conversation is NOT re-keyed
49
+ * on upgrade. A custom or `#N`-suffixed name → a name-scoped id (same formula
50
+ * the cwd-lock uses).
51
+ *
52
+ * Using the ASSIGNED leaf name (the broker's `#N` on collision) disambiguates
53
+ * even two unnamed agents: the 1st stays `folder` (== default → legacy room),
54
+ * the 2nd becomes `folder#2` (≠ default → name-scoped room).
55
+ *
56
+ * INVARIANT: every callsite that derives the App↔Pi room for the same agent
57
+ * MUST go through this function — otherwise the app would pair on a room the
58
+ * Pi never announces.
59
+ */
60
+ export function roomIdFor(cwd, name) {
61
+ if (!name || name === defaultAgentName(cwd))
62
+ return roomIdForCwd(cwd);
63
+ let target;
64
+ try {
65
+ target = realpathSync(cwd);
66
+ }
67
+ catch {
68
+ target = cwd;
69
+ }
70
+ // NUL separator (U+0000): impossible in a POSIX path and stripped from any
71
+ // sanitized name, so the cwd/name boundary is unambiguous.
72
+ const sep = String.fromCharCode(0);
73
+ return createHash("sha256")
74
+ .update(target + sep + name)
75
+ .digest("base64url")
76
+ .slice(0, 12);
77
+ }
78
+ //# sourceMappingURL=rooms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rooms.js","sourceRoot":"","sources":["../src/rooms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;QACrE,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;IACD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,OAAO,UAAU,CAAC,QAAQ,CAAC;SACxB,MAAM,CAAC,SAAS,CAAC;SACjB,MAAM,CAAC,WAAW,CAAC;SACnB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,IAAa;IAClD,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,gBAAgB,CAAC,GAAG,CAAC;QAAE,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;IACtE,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;IACD,2EAA2E;IAC3E,2DAA2D;IAC3D,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACnC,OAAO,UAAU,CAAC,QAAQ,CAAC;SACxB,MAAM,CAAC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC;SAC3B,MAAM,CAAC,WAAW,CAAC;SACnB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { Broker } from "./broker.js";
2
+ import { BrokerRemote } from "./broker_remote.js";
3
+ import { PiForwardClient } from "../transport/pi_forward_client.js";
4
+ import type { RelayClient } from "../transport/relay_client.js";
5
+ import { type MeshTopologySnapshot } from "../mesh/siblings.js";
6
+ import type { Ed25519Keypair } from "../pairing/crypto.js";
7
+ /**
8
+ * Cross-PC mesh bridge composition. Discovery finishes before either transport
9
+ * half is constructed, and the returned bridge stays dormant until its caller
10
+ * has re-checked lifecycle ownership and calls `activate()`.
11
+ */
12
+ export interface AttachBridgeOptions {
13
+ /** The leader's local Broker (from SessionPeer.localBroker()). */
14
+ broker: Broker;
15
+ /** Live relay connection. Caller owns its lifecycle. */
16
+ relay: RelayClient;
17
+ /** Relay URL in http(s):// form — for standalone topology discovery. */
18
+ relayUrl: string;
19
+ /** This host's Ed25519 identity (machine Pi-key). */
20
+ keypair: Ed25519Keypair;
21
+ /** Retained Pi-produced topology. Supplying it bypasses discovery. */
22
+ topology?: MeshTopologySnapshot;
23
+ /** Standalone discovery deadline per mesh request. Defaults to 5 seconds. */
24
+ meshRequestTimeoutMs?: number;
25
+ /** Diagnostic logger. Defaults to a no-op (avoids TUI leaks). */
26
+ log?: (msg: string) => void;
27
+ }
28
+ export interface CrossPcBridge {
29
+ brokerRemote: BrokerRemote;
30
+ piForward: PiForwardClient;
31
+ topology: MeshTopologySnapshot;
32
+ /** Publish the already-built router exactly once. */
33
+ activate(): void;
34
+ /** Safe before or after activation; tears down both halves exactly once. */
35
+ detach(): void;
36
+ }
37
+ export declare function attachCrossPcBridge(opts: AttachBridgeOptions): Promise<CrossPcBridge>;