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 +15 -1
- package/README.md +4 -5
- package/index.ts +22 -117
- package/package.json +1 -1
- package/sync.ffs_db +0 -0
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-
|
|
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
|
|
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 **
|
|
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
|
|
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
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
package/sync.ffs_db
CHANGED
|
Binary file
|