@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,48 @@
1
+ /**
2
+ * Plan/25 Wave D — pure formatter for the `/unbien peers` output.
3
+ *
4
+ * Lives in its own module so it can be unit-tested without booting the full
5
+ * extension (no keychain access, no relay client, no mocks needed). The
6
+ * extension imports the function and wraps it with the `_sessionPeer`
7
+ * `list_peers` request.
8
+ *
9
+ * Input shape: the broker's `list_peers_reply` body returns peer names as
10
+ * plain strings — local peers without a prefix (`sess-1`, `agent-2`),
11
+ * cross-PC peers with a `<pc_label>:<peer>` prefix (`casa:sess-3`).
12
+ */
13
+ export function formatPeerInventory(peers, selfName) {
14
+ const locals = [];
15
+ const remotes = new Map();
16
+ for (const p of peers) {
17
+ if (selfName && p === selfName)
18
+ continue;
19
+ const idx = p.indexOf(":");
20
+ if (idx > 0 && idx < p.length - 1) {
21
+ const label = p.slice(0, idx);
22
+ const name = p.slice(idx + 1);
23
+ const bucket = remotes.get(label) ?? [];
24
+ bucket.push(name);
25
+ remotes.set(label, bucket);
26
+ }
27
+ else {
28
+ locals.push(p);
29
+ }
30
+ }
31
+ const lines = [" local:"];
32
+ if (locals.length === 0) {
33
+ lines.push(" (none)");
34
+ }
35
+ else {
36
+ for (const n of locals.sort())
37
+ lines.push(` ${n}`);
38
+ }
39
+ const sortedLabels = [...remotes.keys()].sort();
40
+ for (const label of sortedLabels) {
41
+ lines.push("");
42
+ lines.push(` remote:${label}`);
43
+ for (const n of (remotes.get(label) ?? []).sort())
44
+ lines.push(` ${n}`);
45
+ }
46
+ return lines.join("\n");
47
+ }
48
+ //# sourceMappingURL=peer_inventory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer_inventory.js","sourceRoot":"","sources":["../../src/session/peer_inventory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,UAAU,mBAAmB,CACjC,KAAe,EACf,QAAiB;IAEjB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,QAAQ,IAAI,CAAC,KAAK,QAAQ;YAAE,SAAS;QACzC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAC9B,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAa,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC3B,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,YAAY,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC;QAChC,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { PeerInfo } from "./broker.js";
2
+ /** Shared wire limits measured in JavaScript UTF-16 code units (`.length`). */
3
+ export declare const MAX_PEERS_UPDATE_ENTRIES = 1024;
4
+ export declare const MAX_CWD_LENGTH = 4096;
5
+ export declare const MAX_NAME_LENGTH = 256;
6
+ export declare const MAX_ADDRESS_LENGTH = 4352;
7
+ export declare function isBoundedPeerInfo(value: unknown): value is PeerInfo;
8
+ export declare function isBoundedPeerRoster(infos: readonly PeerInfo[]): boolean;
9
+ export declare function isBoundedPeerAddresses(addresses: readonly unknown[]): boolean;
@@ -0,0 +1,24 @@
1
+ /** Shared wire limits measured in JavaScript UTF-16 code units (`.length`). */
2
+ export const MAX_PEERS_UPDATE_ENTRIES = 1024;
3
+ export const MAX_CWD_LENGTH = 4096;
4
+ export const MAX_NAME_LENGTH = 256;
5
+ export const MAX_ADDRESS_LENGTH = 4352;
6
+ export function isBoundedPeerInfo(value) {
7
+ if (!value || typeof value !== "object")
8
+ return false;
9
+ const { cwd, name, address } = value;
10
+ return (typeof cwd === "string" &&
11
+ cwd.length <= MAX_CWD_LENGTH &&
12
+ typeof name === "string" &&
13
+ name.length <= MAX_NAME_LENGTH &&
14
+ typeof address === "string" &&
15
+ address.length <= MAX_ADDRESS_LENGTH);
16
+ }
17
+ export function isBoundedPeerRoster(infos) {
18
+ return (infos.length <= MAX_PEERS_UPDATE_ENTRIES && infos.every(isBoundedPeerInfo));
19
+ }
20
+ export function isBoundedPeerAddresses(addresses) {
21
+ return (addresses.length <= MAX_PEERS_UPDATE_ENTRIES &&
22
+ addresses.every((address) => typeof address === "string" && address.length <= MAX_ADDRESS_LENGTH));
23
+ }
24
+ //# sourceMappingURL=peer_limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer_limits.js","sourceRoot":"","sources":["../../src/session/peer_limits.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AACnC,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AACnC,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEvC,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAAiB,CAAC;IACjD,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,CAAC,MAAM,IAAI,cAAc;QAC5B,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,CAAC,MAAM,IAAI,eAAe;QAC9B,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO,CAAC,MAAM,IAAI,kBAAkB,CACrC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAA0B;IAC5D,OAAO,CACL,KAAK,CAAC,MAAM,IAAI,wBAAwB,IAAI,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAC3E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,SAA6B;IAClE,OAAO,CACL,SAAS,CAAC,MAAM,IAAI,wBAAwB;QAC5C,SAAS,CAAC,KAAK,CACb,CAAC,OAAO,EAAE,EAAE,CACV,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,IAAI,kBAAkB,CACtE,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,108 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ /**
3
+ * One mesh-envelope wrapper message (docs/rpc-envelope.md). `type` is the
4
+ * PROTOCOL-NAMESPACE discriminator (NOT direction) and names the payload field:
5
+ * "rpc" -> `.rpc` — byte-faithful pi rpc frame (pi's rpc handler acts).
6
+ * "evt" -> `.evt` — forwarded pi bus event (app view plane).
7
+ * "ub" -> `.ub` — un-bien's OWN protocol; inner `.type` = hello /
8
+ * session_sync / session_launch / ...; the inner type +
9
+ * receiver role decide who acts and which direction.
10
+ * Direction is carried by the inner frame `.type` + receiver role, never `type`.
11
+ * `ts` (epoch ms) and `protocolVersion` (decode-guard) are cross-cutting and stay
12
+ * top-level. Each plane now stamps its real type ("rpc"/"evt"/"ub"); legacy
13
+ * `"env"` is still ACCEPTED on read for one transition, never stamped.
14
+ */
15
+ export interface EnvelopeMessage {
16
+ type?: string;
17
+ ts?: number;
18
+ /** Envelope/pi-rpc protocol version for client decode-guarding. Cross-cutting. */
19
+ protocolVersion?: number;
20
+ rpc?: unknown;
21
+ /** un-bien display sidecar riding ALONGSIDE `rpc` in the same envelope. For
22
+ * edit-family `tool_execution_start` frames it carries `{ hunks }` display
23
+ * diff data; the `rpc` frame itself stays byte-faithful (raw args). */
24
+ aux?: {
25
+ hunks?: unknown[];
26
+ } & Record<string, unknown>;
27
+ evt?: {
28
+ channel: string;
29
+ data: unknown;
30
+ };
31
+ /** un-bien's own protocol plane (`type:"ub"`). The inner frame is one of
32
+ * ``UbFrame`` — handshake fields (caps, sessionId) nest in the `hello`
33
+ * variant, NOT at the envelope top level. */
34
+ ub?: UbFrame;
35
+ }
36
+ /**
37
+ * The un-bien plane's inner frames — un-bien's OWN protocol (we own these, so
38
+ * they are typed, unlike the opaque byte-faithful pi `rpc` frame). The inner
39
+ * `.type` + receiver role decide who acts and which direction it flows.
40
+ */
41
+ export type UbFrame = {
42
+ type: "hello";
43
+ caps: string[];
44
+ sessionId?: string;
45
+ } | {
46
+ type: "session_sync";
47
+ id?: string;
48
+ limit?: number;
49
+ } | {
50
+ type: "session_sync_end";
51
+ in_reply_to?: string;
52
+ session_started_at?: number;
53
+ truncated?: boolean;
54
+ } | {
55
+ type: "session_launch";
56
+ id?: string;
57
+ mode: string;
58
+ cwd?: string;
59
+ name?: string;
60
+ };
61
+ /** Legacy wrapper marker for the rpc/evt plane — still stamped + accepted during
62
+ * the transition (the explicit rpc/evt namespace split is a later wave). */
63
+ export declare const ENVELOPE_KIND = "env";
64
+ /** un-bien-owned plane wrapper marker. */
65
+ export declare const UB_KIND = "ub";
66
+ /** pi-owned plane wrapper markers: the rpc spine + the evt view plane. */
67
+ export declare const RPC_KIND = "rpc";
68
+ export declare const EVT_KIND = "evt";
69
+ /**
70
+ * True if a decoded inbound object is a mesh-envelope frame (any plane) rather
71
+ * than a stock ClientMessage — by REAL wrapper type ("rpc"/"evt"/"ub", or legacy
72
+ * "env" accepted one transition) OR by payload FIELD-PRESENCE. The single source
73
+ * of truth for both inbound guards (PlainPeerChannel._onLine + index.ts's
74
+ * reconnect attach path) so they cannot drift — kept intentionally permissive so
75
+ * it routes correctly regardless of which plane's frame arrives first.
76
+ */
77
+ export declare function isEnvelopeFrame(obj: Record<string, unknown>): boolean;
78
+ /** un-bien-plane INNER frame type for the capability handshake. */
79
+ export declare const HELLO_KIND = "hello";
80
+ /**
81
+ * Build the capability handshake sent to a peer on attach — the envelope-native
82
+ * replacement for the stock `session_history.capabilities`. Rides the un-bien
83
+ * plane (`type:"ub"`) as a `hello` inner frame the APP handles; caps + sessionId
84
+ * nest inside it, protocolVersion stays top-level.
85
+ */
86
+ export declare function helloEnvelope(caps: string[], sessionId?: string, protocolVersion?: number): EnvelopeMessage;
87
+ type Payload = Record<string, unknown>;
88
+ /** Event names we register a `pi.on()` cue for (the live plane). */
89
+ export declare const RPC_EVENT_NAMES: readonly string[];
90
+ /**
91
+ * Pure map: a `pi.on()` event (name + payload) → its rpc-envelope `{ rpc }`
92
+ * message, or `null` when the event carries no streamed frame. Exported for
93
+ * conformance tests against the captured `pi --mode rpc` corpus.
94
+ */
95
+ export declare function envelopeForEvent(name: string, payload: Payload): EnvelopeMessage | null;
96
+ /**
97
+ * Register the live-plane `pi.on()` handlers; each fires `broadcast` with the
98
+ * rpc-envelope frame. Returns a handle whose `dispose()` stops broadcasting
99
+ * (pi.on has no unregister, so we gate on a disposed flag).
100
+ */
101
+ export declare function createRpcEnvelope(pi: ExtensionAPI, broadcast: (env: EnvelopeMessage) => void, opts?: {
102
+ enrichArgs?: (tool: string, args: unknown) => {
103
+ hunks: unknown[];
104
+ } | null;
105
+ }): {
106
+ dispose(): void;
107
+ };
108
+ export {};
@@ -0,0 +1,221 @@
1
+ // Reconstruct pi's `--mode rpc` EVENT PLANE in-process from `pi.on()` and
2
+ // broadcast each frame as an rpc-envelope `{ rpc }` message.
3
+ //
4
+ // `pi --mode rpc` builds its event stream by subscribing to the session and
5
+ // piping every `AgentSessionEvent` through `toJsonEvent` (pi
6
+ // modes/rpc/rpc-mode.ts:355 `session.subscribe(e => output(toJsonEvent(e)))`).
7
+ // An in-process extension can't call `session.subscribe` (host-only), but
8
+ // `pi.on()` delivers the same underlying events — so we source them from the
9
+ // bus instead and emit byte-faithful rpc frames. The only streamed frame
10
+ // `pi.on()` can't give is `entry_appended` (subscribe-only); that's the
11
+ // getEntries reconstruction path, deferred. See docs/rpc-on-event-map.md.
12
+ //
13
+ // Note: extension `on()` payloads are a SUPERSET of the rpc/AgentEvent frame
14
+ // (e.g. `turn_start` carries `turnIndex`/`timestamp` the rpc frame lacks), so
15
+ // each builder selects exactly the rpc fields rather than passing the payload
16
+ // through.
17
+ //
18
+ // ⚠️ DRIFT HAZARD — THE FRAME SHAPES BELOW MUST MATCH pi's `--mode rpc` OUTPUT.
19
+ // These builders + `toJsonEvent` reproduce what pi writes to rpc stdout
20
+ // (pi modes/json-event.ts `toJsonEvent` + modes/rpc/rpc-mode.ts `output(...)`,
21
+ // pi 0.84.3). We CANNOT call pi's own function: `@earendil-works/pi-coding-agent`
22
+ // ships an `exports` map that exposes ONLY `.` (dist/index.js), there is no
23
+ // `json-event` module in `dist`, and the `toJsonEvent` symbol appears nowhere
24
+ // in the published package (inlined away) — so it is neither importable nor
25
+ // deep-importable. This is therefore a HAND-KEPT MIRROR. If pi changes a wire
26
+ // frame, this drifts silently. The guard is CONFORMANCE against the captured
27
+ // real `pi --mode rpc` corpus (un-bien Tests/Fixtures/rpc-stream/*, the actual
28
+ // pi bytes; see docs/rpc-on-event-map.md + the cross-repo corpus plan item):
29
+ // replay those and assert our output matches. Update BOTH this file and the
30
+ // fixtures together when bumping the pinned pi version.
31
+ /** Legacy wrapper marker for the rpc/evt plane — still stamped + accepted during
32
+ * the transition (the explicit rpc/evt namespace split is a later wave). */
33
+ export const ENVELOPE_KIND = "env";
34
+ /** un-bien-owned plane wrapper marker. */
35
+ export const UB_KIND = "ub";
36
+ /** pi-owned plane wrapper markers: the rpc spine + the evt view plane. */
37
+ export const RPC_KIND = "rpc";
38
+ export const EVT_KIND = "evt";
39
+ /**
40
+ * True if a decoded inbound object is a mesh-envelope frame (any plane) rather
41
+ * than a stock ClientMessage — by REAL wrapper type ("rpc"/"evt"/"ub", or legacy
42
+ * "env" accepted one transition) OR by payload FIELD-PRESENCE. The single source
43
+ * of truth for both inbound guards (PlainPeerChannel._onLine + index.ts's
44
+ * reconnect attach path) so they cannot drift — kept intentionally permissive so
45
+ * it routes correctly regardless of which plane's frame arrives first.
46
+ */
47
+ export function isEnvelopeFrame(obj) {
48
+ return (obj.type === RPC_KIND ||
49
+ obj.type === EVT_KIND ||
50
+ obj.type === UB_KIND ||
51
+ obj.type === ENVELOPE_KIND ||
52
+ obj.rpc !== undefined ||
53
+ obj.evt !== undefined ||
54
+ obj.ub !== undefined);
55
+ }
56
+ /** un-bien-plane INNER frame type for the capability handshake. */
57
+ export const HELLO_KIND = "hello";
58
+ /**
59
+ * Build the capability handshake sent to a peer on attach — the envelope-native
60
+ * replacement for the stock `session_history.capabilities`. Rides the un-bien
61
+ * plane (`type:"ub"`) as a `hello` inner frame the APP handles; caps + sessionId
62
+ * nest inside it, protocolVersion stays top-level.
63
+ */
64
+ export function helloEnvelope(caps, sessionId, protocolVersion = 1) {
65
+ const hello = sessionId
66
+ ? { type: "hello", caps, sessionId }
67
+ : { type: "hello", caps };
68
+ return { type: UB_KIND, protocolVersion, ub: hello };
69
+ }
70
+ /**
71
+ * Drop the cumulative `partial` snapshot from a streaming assistant delta so
72
+ * wire size stays linear; a `toolcall_start` keeps the constant-sized `id` +
73
+ * `toolName`. Defensive (no throws) — this runs inside an SDK event callback.
74
+ *
75
+ * ⚠️ MUST MATCH pi `modes/json-event.ts` `toJsonAssistantMessageEvent` byte for
76
+ * byte (unexported — see the module header). Verified against the real
77
+ * `pi --mode rpc` capture; keep the conformance fixtures in lockstep.
78
+ */
79
+ function stripPartial(ame) {
80
+ if (ame.type === "toolcall_start") {
81
+ const toolCall = ame.partial?.content?.[ame.contentIndex ?? -1];
82
+ const { partial: _p, ...rest } = ame;
83
+ return { ...rest, id: toolCall?.id, toolName: toolCall?.name };
84
+ }
85
+ if (!("partial" in ame))
86
+ return { ...ame };
87
+ const { partial: _p, ...rest } = ame;
88
+ return rest;
89
+ }
90
+ const BUILDERS = {
91
+ agent_start: () => ({ type: "agent_start" }),
92
+ agent_end: (p) => ({
93
+ type: "agent_end",
94
+ messages: p.messages,
95
+ willRetry: p.willRetry ?? false,
96
+ }),
97
+ agent_settled: () => ({ type: "agent_settled" }),
98
+ turn_start: () => ({ type: "turn_start" }),
99
+ turn_end: (p) => ({
100
+ type: "turn_end",
101
+ message: p.message,
102
+ toolResults: p.toolResults ?? [],
103
+ }),
104
+ message_start: (p) => ({ type: "message_start", message: p.message }),
105
+ message_update: (p) => ({
106
+ type: "message_update",
107
+ usage: p.message?.usage,
108
+ assistantMessageEvent: stripPartial((p.assistantMessageEvent ?? {})),
109
+ }),
110
+ message_end: (p) => ({ type: "message_end", message: p.message }),
111
+ tool_execution_start: (p) => ({
112
+ type: "tool_execution_start",
113
+ toolCallId: p.toolCallId,
114
+ toolName: p.toolName,
115
+ args: p.args,
116
+ }),
117
+ tool_execution_update: (p) => ({
118
+ type: "tool_execution_update",
119
+ toolCallId: p.toolCallId,
120
+ toolName: p.toolName,
121
+ args: p.args,
122
+ partialResult: p.partialResult,
123
+ }),
124
+ tool_execution_end: (p) => ({
125
+ type: "tool_execution_end",
126
+ toolCallId: p.toolCallId,
127
+ toolName: p.toolName,
128
+ result: p.result,
129
+ isError: p.isError ?? false,
130
+ }),
131
+ // DEAD ON THE LIVE PLANE — pi does NOT deliver queue_update to extensions.
132
+ // AgentSession._emitQueueUpdate() (pi agent-session.ts) calls only this._emit()
133
+ // (the host `subscribe` stream that `pi --mode rpc` consumes) and NEVER
134
+ // this._extensionRunner.emit(), so pi.on("queue_update") registered from
135
+ // RPC_EVENT_NAMES NEVER FIRES. (Every other event we forward IS fanned to
136
+ // extensions via _emitExtensionEvent(); queue_update is the lone exception, and
137
+ // the ExtensionAPI exposes only hasPendingMessages():boolean — no queue text,
138
+ // no subscribe.) So the fork CANNOT send a queue snapshot; the queued display
139
+ // is APP-OWNED (optimistic chip cleared on model-consumption). Kept only to
140
+ // document the frame shape the app's (now dead) handler once consumed. See
141
+ // design 01M158S7.
142
+ queue_update: (p) => ({
143
+ type: "queue_update",
144
+ steering: p.steering ?? [],
145
+ followUp: p.followUp ?? [],
146
+ }),
147
+ };
148
+ // The extension fires `session_compact` (not the subscribe-only `compaction_end`)
149
+ // with the persisted `compactionEntry`. Remap to the rpc `compaction_end` frame
150
+ // the consumer renders. See docs/rpc-on-event-map.md table A.
151
+ function compactionEndFrame(p) {
152
+ const entry = p.compactionEntry;
153
+ return {
154
+ type: "compaction_end",
155
+ reason: p.reason,
156
+ result: entry
157
+ ? { summary: entry.summary, tokensBefore: entry.tokensBefore }
158
+ : null,
159
+ aborted: false,
160
+ willRetry: p.willRetry ?? false,
161
+ };
162
+ }
163
+ /** Event names we register a `pi.on()` cue for (the live plane). */
164
+ export const RPC_EVENT_NAMES = [
165
+ ...Object.keys(BUILDERS),
166
+ "session_compact",
167
+ ];
168
+ /**
169
+ * Pure map: a `pi.on()` event (name + payload) → its rpc-envelope `{ rpc }`
170
+ * message, or `null` when the event carries no streamed frame. Exported for
171
+ * conformance tests against the captured `pi --mode rpc` corpus.
172
+ */
173
+ export function envelopeForEvent(name, payload) {
174
+ if (name === "session_compact")
175
+ return { rpc: compactionEndFrame(payload) };
176
+ const build = BUILDERS[name];
177
+ return build ? { rpc: build(payload) } : null;
178
+ }
179
+ /**
180
+ * Register the live-plane `pi.on()` handlers; each fires `broadcast` with the
181
+ * rpc-envelope frame. Returns a handle whose `dispose()` stops broadcasting
182
+ * (pi.on has no unregister, so we gate on a disposed flag).
183
+ */
184
+ export function createRpcEnvelope(pi, broadcast, opts) {
185
+ let disposed = false;
186
+ // SAFETY: pi.on's public typing is a narrower event-name union; the live
187
+ // plane subscribes by string name, which is valid at runtime for every
188
+ // RPC_EVENT_NAMES entry (they are real AgentSessionEvent names).
189
+ const on = pi.on;
190
+ for (const name of RPC_EVENT_NAMES) {
191
+ on(name, (payload) => {
192
+ if (disposed)
193
+ return;
194
+ const p = (payload ?? {});
195
+ let env = envelopeForEvent(name, p);
196
+ if (!env)
197
+ return;
198
+ if (name === "tool_execution_start" && opts?.enrichArgs) {
199
+ const enriched = opts.enrichArgs(p.toolName, p.args);
200
+ if (enriched && Array.isArray(enriched.hunks)) {
201
+ env = { ...env, aux: { hunks: enriched.hunks } };
202
+ }
203
+ }
204
+ // OUTPUT enrichment is app-side (design 01M177AF): the app classifies the
205
+ // persisted result in its reducer (live + get_entries replay). No
206
+ // aux.output is emitted here; only aux.hunks (input Edit diff) rides live.
207
+ try {
208
+ broadcast(env);
209
+ }
210
+ catch {
211
+ /* best-effort: never let a broadcast error escape into the SDK callback */
212
+ }
213
+ });
214
+ }
215
+ return {
216
+ dispose() {
217
+ disposed = true;
218
+ },
219
+ };
220
+ }
221
+ //# sourceMappingURL=rpc_envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc_envelope.js","sourceRoot":"","sources":["../../src/session/rpc_envelope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { SessionEntry } from "@earendil-works/pi-coding-agent";
2
+ import type { EnvelopeMessage } from "./rpc_envelope.js";
3
+ /** Native pi `get_entries` result: the raw entry log + the leaf cursor to
4
+ * resume from. The app reduces `entries` into its transcript itself. */
5
+ export interface GetEntriesResult {
6
+ entries: SessionEntry[];
7
+ leafId: string | null;
8
+ }
9
+ /** Build a `{ rpc: response }` envelope. Correlates by the command's `id`. */
10
+ export declare function rpcResponse(command: string, id: string | undefined, result: {
11
+ success: boolean;
12
+ data?: unknown;
13
+ error?: string;
14
+ }): EnvelopeMessage;
15
+ /** SDK-facing operations the dispatcher needs; wired to fork primitives. */
16
+ export interface RpcCommandHandlers {
17
+ prompt(message: string, opts: {
18
+ id?: string;
19
+ images?: unknown;
20
+ streamingBehavior?: string;
21
+ }): Promise<void>;
22
+ steer(message: string, opts: {
23
+ images?: unknown;
24
+ }): Promise<void>;
25
+ followUp(message: string, opts: {
26
+ images?: unknown;
27
+ }): Promise<void>;
28
+ abort(): Promise<void>;
29
+ /** Switch the live model; resolves to the wire Model object for the response. */
30
+ setModel(provider: string, modelId: string): Promise<unknown>;
31
+ /** Set the thinking (reasoning-effort) level. */
32
+ setThinkingLevel(level: string): Promise<void>;
33
+ /** List configured models (pi `get_available_models`); resolves to Model[].
34
+ * Optional while the surface is being wired — an unset handler falls through
35
+ * to the transitional stock path (returns null from dispatch). */
36
+ getAvailableModels?(): Promise<unknown>;
37
+ /** Manually compact context (pi `compact`); resolves to the CompactionResult. */
38
+ compact?(customInstructions?: string): Promise<unknown>;
39
+ /** Start a fresh session (pi `new_session`); resolves to {cancelled}. */
40
+ newSession?(parentSession?: string): Promise<unknown>;
41
+ /** Clear the steering/follow-up queue (pi `clear_queue`); resolves to the
42
+ * removed {steering, followUp} text. */
43
+ clearQueue?(): Promise<unknown>;
44
+ /** Return the session ENTRY log (pi `get_entries`); resolves to
45
+ * `{ entries, leafId }`. `since` slices to entries AFTER that entry id
46
+ * (the native delta cursor). This is the transcript source — the app
47
+ * reduces the raw entries itself; the fork does NOT replay them. */
48
+ getEntries?(since?: string): Promise<GetEntriesResult>;
49
+ }
50
+ /**
51
+ * Map an inbound rpc command frame to an SDK call and produce the response
52
+ * envelope. Returns `null` for commands we don't handle yet (ignored —
53
+ * forward-compatible). A handler throw becomes `success:false` with the message.
54
+ */
55
+ export declare function dispatchRpcCommand(frame: Record<string, unknown>, handlers: RpcCommandHandlers): Promise<EnvelopeMessage | null>;
@@ -0,0 +1,136 @@
1
+ // App -> fork inbound: interpret an envelope-carried pi `RpcCommand` and drive
2
+ // the SDK, then answer with a `{ rpc: { type:"response", ... } }` envelope back
3
+ // to the sender. This is the NEW-protocol-native inbound path — it does NOT go
4
+ // through the stock `ClientMessage` / `_routeClientMessageFrom` switch (that,
5
+ // with its `error`/`cancelled`/`pong` ServerMessage replies, is old protocol,
6
+ // retired). The SDK primitives the handlers wrap (sendUserMessage/_wakeAgent,
7
+ // ctx.abort) are pi itself, not old protocol.
8
+ //
9
+ // Command shapes: pi.dev/docs/latest/rpc. v1 surface = prompt / steer /
10
+ // follow_up / abort / set_model / set_thinking_level. get_state / get_entries /
11
+ // compact / bash follow (they need the fuller ctx.sessionManager).
12
+ /** Build a `{ rpc: response }` envelope. Correlates by the command's `id`. */
13
+ export function rpcResponse(command, id, result) {
14
+ const frame = {
15
+ type: "response",
16
+ command,
17
+ success: result.success,
18
+ };
19
+ if (id !== undefined)
20
+ frame.id = id;
21
+ if (result.data !== undefined)
22
+ frame.data = result.data;
23
+ if (result.error !== undefined)
24
+ frame.error = result.error;
25
+ return { rpc: frame };
26
+ }
27
+ function str(v) {
28
+ return typeof v === "string" ? v : "";
29
+ }
30
+ /**
31
+ * Map an inbound rpc command frame to an SDK call and produce the response
32
+ * envelope. Returns `null` for commands we don't handle yet (ignored —
33
+ * forward-compatible). A handler throw becomes `success:false` with the message.
34
+ */
35
+ export async function dispatchRpcCommand(frame, handlers) {
36
+ const command = typeof frame.type === "string" ? frame.type : undefined;
37
+ if (!command)
38
+ return null;
39
+ const id = typeof frame.id === "string" ? frame.id : undefined;
40
+ try {
41
+ switch (command) {
42
+ case "prompt":
43
+ await handlers.prompt(str(frame.message), {
44
+ id,
45
+ images: frame.images,
46
+ streamingBehavior: typeof frame.streamingBehavior === "string"
47
+ ? frame.streamingBehavior
48
+ : undefined,
49
+ });
50
+ return rpcResponse("prompt", id, { success: true });
51
+ case "steer":
52
+ await handlers.steer(str(frame.message), {
53
+ images: frame.images,
54
+ });
55
+ return rpcResponse("steer", id, { success: true });
56
+ case "follow_up":
57
+ await handlers.followUp(str(frame.message), {
58
+ images: frame.images,
59
+ });
60
+ return rpcResponse("follow_up", id, { success: true });
61
+ case "abort":
62
+ await handlers.abort();
63
+ return rpcResponse("abort", id, { success: true });
64
+ case "set_model": {
65
+ const data = await handlers.setModel(str(frame.provider), str(frame.modelId));
66
+ return rpcResponse("set_model", id, {
67
+ success: true,
68
+ data,
69
+ });
70
+ }
71
+ case "set_thinking_level":
72
+ await handlers.setThinkingLevel(str(frame.level));
73
+ return rpcResponse("set_thinking_level", id, {
74
+ success: true,
75
+ });
76
+ case "get_available_models": {
77
+ if (!handlers.getAvailableModels)
78
+ return null;
79
+ const data = await handlers.getAvailableModels();
80
+ return rpcResponse("get_available_models", id, {
81
+ success: true,
82
+ data,
83
+ });
84
+ }
85
+ case "compact": {
86
+ if (!handlers.compact)
87
+ return null;
88
+ const data = await handlers.compact(typeof frame.customInstructions === "string"
89
+ ? frame.customInstructions
90
+ : undefined);
91
+ return rpcResponse("compact", id, {
92
+ success: true,
93
+ data,
94
+ });
95
+ }
96
+ case "new_session": {
97
+ if (!handlers.newSession)
98
+ return null;
99
+ const data = await handlers.newSession(typeof frame.parentSession === "string"
100
+ ? frame.parentSession
101
+ : undefined);
102
+ return rpcResponse("new_session", id, {
103
+ success: true,
104
+ data,
105
+ });
106
+ }
107
+ case "clear_queue": {
108
+ if (!handlers.clearQueue)
109
+ return null;
110
+ const data = await handlers.clearQueue();
111
+ return rpcResponse("clear_queue", id, {
112
+ success: true,
113
+ data,
114
+ });
115
+ }
116
+ case "get_entries": {
117
+ if (!handlers.getEntries)
118
+ return null;
119
+ const data = await handlers.getEntries(typeof frame.since === "string" ? frame.since : undefined);
120
+ return rpcResponse("get_entries", id, {
121
+ success: true,
122
+ data,
123
+ });
124
+ }
125
+ default:
126
+ return null; // unhandled command: ignore (forward-compat)
127
+ }
128
+ }
129
+ catch (err) {
130
+ return rpcResponse(command, id, {
131
+ success: false,
132
+ error: err instanceof Error ? err.message : String(err),
133
+ });
134
+ }
135
+ }
136
+ //# sourceMappingURL=rpc_inbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc_inbound.js","sourceRoot":"","sources":["../../src/session/rpc_inbound.ts"],"names":[],"mappings":"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"}