pi-link 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,7 +2,21 @@
2
2
 
3
3
  All notable changes to pi-link are documented here.
4
4
 
5
- This changelog is based on the git history from `2026-03-21` through `2026-04-02` (current). Versions correspond to npm publishes.
5
+ This changelog is based on the git history from `2026-03-21` through `2026-04-03` (current). Versions correspond to npm publishes.
6
+
7
+ ---
8
+
9
+ ## 0.1.6 — 2026-04-03
10
+
11
+ ### Added
12
+
13
+ - **Persistent connection intent.** `/link-connect` and `/link-disconnect` now save their state to the session via `pi.appendEntry("link-active", ...)`. On `session_start`, the saved preference is checked before falling back to `--link`. Connect once and it stays connected across session resumes without needing the flag.
14
+
15
+ ### Removed
16
+
17
+ - **`cwd_update` message type.** Working directories are now only reported on connect (via `register`/`welcome`), not mid-session. Protocol returns to 9 message types.
18
+
19
+ - **`session_switch` handler.** Removed the session-switch logic that handled name and cwd changes on `/resume`. Simplifies the lifecycle.
6
20
 
7
21
  ---
8
22
 
package/README.md CHANGED
@@ -191,7 +191,7 @@ Send a prompt to a remote terminal and **wait** for the LLM's response (synchron
191
191
 
192
192
  Lists all connected terminals with role info, live agent status, working directory, and self-identification. Takes no parameters.
193
193
 
194
- Each terminal reports its current working directory on connect and on session switch. `link_list` shows the full absolute path so agents can choose the right target, use explicit paths when terminals differ, and catch wrong-project mistakes early.
194
+ Each terminal reports its current working directory on connect. `link_list` shows the full absolute path so agents can choose the right target, use explicit paths when terminals differ, and catch wrong-project mistakes early.
195
195
 
196
196
  Each terminal's status is derived automatically from Pi lifecycle events — agents can't set it manually. Three states:
197
197
 
@@ -397,7 +397,7 @@ The `pi.extensions` field tells Pi which files to load as extensions. Here it po
397
397
 
398
398
  ### Protocol
399
399
 
400
- The wire protocol consists of **10 message types**, all serialized as JSON over WebSocket frames. New cwd-related fields are optional for backward compatibility.
400
+ The wire protocol consists of **9 message types**, all serialized as JSON over WebSocket frames. Cwd-related fields are optional for backward compatibility.
401
401
 
402
402
  | Type | Direction | Purpose |
403
403
  | ----------------- | --------------- | ----------------------------------------------------------------------- |
@@ -409,7 +409,6 @@ The wire protocol consists of **10 message types**, all serialized as JSON over
409
409
  | `prompt_request` | Any → Any | Request a remote terminal to execute a prompt |
410
410
  | `prompt_response` | Any → Any | Response carrying the remote prompt result |
411
411
  | `status_update` | Any → Hub → All | Terminal broadcasts its agent status change |
412
- | `cwd_update` | Any → Hub → All | Terminal broadcasts a cwd change |
413
412
  | `error` | Hub → Client | Error notification |
414
413
 
415
414
  ### Message Flow Examples
@@ -429,7 +428,7 @@ Client Hub
429
428
  | |
430
429
  ```
431
430
 
432
- Hub then broadcasts `terminal_joined` to the other connected terminals. The `welcome` message includes status and cwd snapshots for all connected terminals (fields omitted above for brevity). `terminal_joined` also includes the new terminal's optional cwd, and mid-session cwd changes are distributed via `cwd_update`.
431
+ Hub then broadcasts `terminal_joined` to the other connected terminals. The `welcome` message includes status and cwd snapshots for all connected terminals (fields omitted above for brevity). `terminal_joined` also includes the new terminal's optional cwd.
433
432
 
434
433
  **Sending a chat message:**
435
434
 
@@ -482,7 +481,7 @@ Default names are random 4-character hex IDs: `t-a1b2`, `t-c3d4`, etc.
482
481
  | `agentRunning` | `boolean` | Whether an agent run is active; blocks incoming remote prompts |
483
482
  | `activeToolName` | `string \| null` | Name of the currently executing tool (drives `tool:<name>` status) |
484
483
  | `stateSince` | `number` | Timestamp of last status change (used for duration display) |
485
- | `currentCwd` | `string` | Current working directory reported to peers on connect and session switch |
484
+ | `currentCwd` | `string` | Current working directory reported to peers on connect |
486
485
  | `manuallyDisconnected` | `boolean` | Set by `/link-disconnect`; suppresses auto-reconnect |
487
486
  | `pendingRemotePrompt` | `object \| null` | Tracks the single in-flight remote prompt execution |
488
487
  | `pendingPromptResponses` | `Map` | Outstanding prompt RPCs awaiting responses (includes inactivity + ceiling timers per entry) |
package/index.ts CHANGED
@@ -49,11 +49,6 @@ interface TerminalJoinedMsg {
49
49
  terminals: string[];
50
50
  cwd?: string;
51
51
  }
52
- interface CwdUpdateMsg {
53
- type: "cwd_update";
54
- name: string;
55
- cwd: string;
56
- }
57
52
  interface TerminalLeftMsg {
58
53
  type: "terminal_left";
59
54
  name: string;
@@ -105,7 +100,6 @@ type LinkMessage =
105
100
  | PromptRequestMsg
106
101
  | PromptResponseMsg
107
102
  | StatusUpdateMsg
108
- | CwdUpdateMsg
109
103
  | ErrorMsg;
110
104
 
111
105
  // ─── Extension ───────────────────────────────────────────────────────────────
@@ -240,17 +234,18 @@ export default function (pi: ExtensionAPI) {
240
234
  return normalized;
241
235
  }
242
236
 
243
- function pushCwdUpdate() {
244
- const msg: CwdUpdateMsg = {
245
- type: "cwd_update",
246
- name: terminalName,
247
- cwd: currentCwd,
248
- };
249
- if (role === "hub") {
250
- hubBroadcast(msg, terminalName);
251
- } else if (role === "client" && ws?.readyState === WebSocket.OPEN) {
252
- ws.send(JSON.stringify(msg));
253
- }
237
+ // ── Connection intent ──────────────────────────────────────────────────
238
+
239
+ function shouldConnect(_ctx: ExtensionContext): boolean {
240
+ const saved = _ctx.sessionManager
241
+ .getEntries()
242
+ .filter(
243
+ (e: { type: string; customType?: string }) =>
244
+ e.type === "custom" && e.customType === "link-active",
245
+ )
246
+ .pop() as { data?: { active?: boolean } } | undefined;
247
+ if (saved?.data?.active !== undefined) return saved.data.active;
248
+ return pi.getFlag("link") === true;
254
249
  }
255
250
 
256
251
  // ── Pending prompt helpers ───────────────────────────────────────────────
@@ -452,10 +447,6 @@ export default function (pi: ExtensionAPI) {
452
447
  resetInactivityFor(msg.name);
453
448
  break;
454
449
 
455
- case "cwd_update":
456
- terminalCwds.set(msg.name, msg.cwd);
457
- break;
458
-
459
450
  // ── Chat message ──
460
451
  case "chat":
461
452
  pi.sendMessage(
@@ -588,21 +579,6 @@ export default function (pi: ExtensionAPI) {
588
579
  return;
589
580
  }
590
581
 
591
- // Cwd update — store and relay to other clients only
592
- if (msg.type === "cwd_update") {
593
- hubTerminalCwds.set(clientName, msg.cwd);
594
- const normalized: CwdUpdateMsg = {
595
- type: "cwd_update",
596
- name: clientName,
597
- cwd: msg.cwd,
598
- };
599
- const json = JSON.stringify(normalized);
600
- for (const [otherWs, name] of hubClients) {
601
- if (name !== clientName) otherWs.send(json);
602
- }
603
- return;
604
- }
605
-
606
582
  // Route chat / prompt messages
607
583
  if (
608
584
  msg.type === "chat" ||
@@ -817,90 +793,13 @@ export default function (pi: ExtensionAPI) {
817
793
  terminalName = preferredName;
818
794
  }
819
795
 
820
- if (pi.getFlag("link") === true) await initialize();
796
+ if (shouldConnect(_ctx)) await initialize();
821
797
  });
822
798
 
823
799
  pi.on("session_shutdown", async () => {
824
800
  cleanup();
825
801
  });
826
802
 
827
- pi.on("session_switch", async (_event, _ctx) => {
828
- ctx = _ctx;
829
-
830
- // 1. Cwd change detection (always, before any name logic)
831
- const newCwd = _ctx.cwd;
832
- const cwdChanged = newCwd !== currentCwd;
833
- if (cwdChanged) currentCwd = newCwd;
834
-
835
- // 2. Restore preferred name from the new session
836
- const saved = _ctx.sessionManager
837
- .getEntries()
838
- .filter(
839
- (e: { type: string; customType?: string }) =>
840
- e.type === "custom" && e.customType === "link-name",
841
- )
842
- .pop() as { data?: { name?: string } } | undefined;
843
-
844
- preferredName = saved?.data?.name ?? null;
845
- const desiredName = preferredName ?? `t-${crypto.randomUUID().slice(0, 4)}`;
846
- const nameChanged = desiredName !== terminalName;
847
-
848
- if (!nameChanged && !cwdChanged) return; // nothing to do
849
-
850
- if (!nameChanged) {
851
- // Name stayed the same, but cwd changed — push cwd update
852
- pushCwdUpdate();
853
- return;
854
- }
855
-
856
- // Name changed (cwd may or may not have changed too)
857
- if (role === "hub") {
858
- // Hub rename in-place — avoid tearing down the server
859
- const takenByOther = Array.from(hubClients.values()).includes(
860
- desiredName,
861
- );
862
- if (takenByOther) {
863
- // Can't use preferred name — keep current identity
864
- ctx?.ui.notify(
865
- `Session preferred name "${desiredName}" is taken, keeping "${terminalName}"`,
866
- "warning",
867
- );
868
- // Still push cwd update under current name if cwd changed
869
- if (cwdChanged) pushCwdUpdate();
870
- return;
871
- }
872
- const old = terminalName;
873
- terminalName = desiredName;
874
- const list = terminalList();
875
- connectedTerminals = list;
876
- updateStatus();
877
- // Notify clients only — hub already updated local state
878
- hubBroadcast(
879
- { type: "terminal_left", name: old, terminals: list },
880
- terminalName,
881
- );
882
- hubBroadcast(
883
- {
884
- type: "terminal_joined",
885
- name: desiredName,
886
- terminals: list,
887
- cwd: currentCwd,
888
- },
889
- terminalName,
890
- );
891
- pushStatus(true);
892
- } else if (role === "client") {
893
- // Client — disconnect and reconnect with new name (register includes cwd)
894
- terminalName = desiredName;
895
- disconnect();
896
- manuallyDisconnected = false;
897
- await initialize();
898
- } else {
899
- // Disconnected — just update local name
900
- terminalName = desiredName;
901
- }
902
- });
903
-
904
803
  pi.on("agent_start", async () => {
905
804
  agentRunning = true;
906
805
  activeToolName = null;
@@ -1384,18 +1283,23 @@ export default function (pi: ExtensionAPI) {
1384
1283
  pi.registerCommand("link-disconnect", {
1385
1284
  description: "Disconnect from the link",
1386
1285
  handler: async (_args, _ctx) => {
1286
+ pi.appendEntry("link-active", { active: false });
1287
+ manuallyDisconnected = true;
1387
1288
  if (role === "disconnected") {
1388
- _ctx.ui.notify("Already disconnected", "info");
1289
+ if (reconnectTimer) {
1290
+ clearTimeout(reconnectTimer);
1291
+ reconnectTimer = null;
1292
+ }
1293
+ _ctx.ui.notify("Link disconnected", "info");
1389
1294
  return;
1390
1295
  }
1391
- manuallyDisconnected = true;
1392
1296
  disconnect();
1393
1297
  _ctx.ui.notify("Disconnected from link", "info");
1394
1298
  },
1395
1299
  });
1396
1300
 
1397
1301
  pi.registerCommand("link-connect", {
1398
- description: "Connect to the link (after manual disconnect)",
1302
+ description: "Connect to the link",
1399
1303
  handler: async (_args, _ctx) => {
1400
1304
  if (role !== "disconnected") {
1401
1305
  _ctx.ui.notify(
@@ -1404,6 +1308,7 @@ export default function (pi: ExtensionAPI) {
1404
1308
  );
1405
1309
  return;
1406
1310
  }
1311
+ pi.appendEntry("link-active", { active: true });
1407
1312
  manuallyDisconnected = false;
1408
1313
  await initialize();
1409
1314
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-link",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "WebSocket-based inter-terminal communication for Pi. Connect multiple Pi terminals over a local link network.",
5
5
  "author": "alvivar",
6
6
  "license": "MIT",
package/sync.ffs_db CHANGED
Binary file