linkshell-cli 0.5.24 → 0.5.26
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/dist/cli/src/runtime/acp/agent-workspace.d.ts +7 -0
- package/dist/cli/src/runtime/acp/agent-workspace.js +81 -1
- package/dist/cli/src/runtime/acp/agent-workspace.js.map +1 -1
- package/dist/cli/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/runtime/acp/agent-workspace.ts +75 -1
- package/web/assets/index-IawebWL8.js +94 -0
- package/web/index.html +1 -1
- package/web/assets/index-Bh3kcNhP.js +0 -94
|
@@ -36,6 +36,13 @@ export declare class AgentWorkspaceProxy {
|
|
|
36
36
|
});
|
|
37
37
|
handleEnvelope(envelope: Envelope): Promise<void>;
|
|
38
38
|
stop(): void;
|
|
39
|
+
private fsWatchers;
|
|
40
|
+
private fsSyncTimer;
|
|
41
|
+
private fsSyncRunning;
|
|
42
|
+
private static readonly FS_SYNC_THROTTLE_MS;
|
|
43
|
+
private startSessionWatchers;
|
|
44
|
+
private scheduleSessionSync;
|
|
45
|
+
private syncAndPushSessions;
|
|
39
46
|
private clientForProvider;
|
|
40
47
|
private shouldRecycleProviderClient;
|
|
41
48
|
private recycleProviderClient;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync, watch } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { basename, join, relative } from "node:path";
|
|
4
4
|
import { createEnvelope, parseTypedPayload, } from "@linkshell/protocol";
|
|
@@ -1660,11 +1660,88 @@ export class AgentWorkspaceProxy {
|
|
|
1660
1660
|
}
|
|
1661
1661
|
}
|
|
1662
1662
|
stop() {
|
|
1663
|
+
for (const watcher of this.fsWatchers) {
|
|
1664
|
+
try {
|
|
1665
|
+
watcher.close();
|
|
1666
|
+
}
|
|
1667
|
+
catch { /* already closed */ }
|
|
1668
|
+
}
|
|
1669
|
+
this.fsWatchers = [];
|
|
1670
|
+
if (this.fsSyncTimer) {
|
|
1671
|
+
clearTimeout(this.fsSyncTimer);
|
|
1672
|
+
this.fsSyncTimer = undefined;
|
|
1673
|
+
}
|
|
1663
1674
|
for (const client of this.clients.values()) {
|
|
1664
1675
|
client.stop();
|
|
1665
1676
|
}
|
|
1666
1677
|
this.clients.clear();
|
|
1667
1678
|
}
|
|
1679
|
+
// ── External-session live refresh ────────────────────────────────────
|
|
1680
|
+
// The console requests the session list only once, at connect. Without this,
|
|
1681
|
+
// a session another `claude`/`codex` process starts (or whose status changes)
|
|
1682
|
+
// after connect never shows up — the tree is frozen at connect time. Watch the
|
|
1683
|
+
// on-disk session stores and re-sync + push when they change, so externally-
|
|
1684
|
+
// driven sessions surface live (this is what CodeIsland does). Event-driven:
|
|
1685
|
+
// zero cost while idle, throttled to at most one re-scan per window during a
|
|
1686
|
+
// burst of writes (an active turn appends constantly).
|
|
1687
|
+
fsWatchers = [];
|
|
1688
|
+
fsSyncTimer;
|
|
1689
|
+
fsSyncRunning = false;
|
|
1690
|
+
static FS_SYNC_THROTTLE_MS = 1500;
|
|
1691
|
+
startSessionWatchers() {
|
|
1692
|
+
if (this.fsWatchers.length > 0)
|
|
1693
|
+
return; // already watching
|
|
1694
|
+
const roots = [
|
|
1695
|
+
join(homedir(), ".claude", "projects"),
|
|
1696
|
+
join(homedir(), ".codex", "sessions"),
|
|
1697
|
+
];
|
|
1698
|
+
for (const root of roots) {
|
|
1699
|
+
if (!existsSync(root))
|
|
1700
|
+
continue;
|
|
1701
|
+
try {
|
|
1702
|
+
// recursive is supported on macOS/Windows; on Linux this throws, and we
|
|
1703
|
+
// simply don't get live updates (the connect-time scan still works).
|
|
1704
|
+
const watcher = watch(root, { recursive: true, persistent: false }, () => this.scheduleSessionSync());
|
|
1705
|
+
watcher.on("error", () => { });
|
|
1706
|
+
this.fsWatchers.push(watcher);
|
|
1707
|
+
}
|
|
1708
|
+
catch {
|
|
1709
|
+
// recursive watch unavailable on this platform — skip silently.
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
scheduleSessionSync() {
|
|
1714
|
+
if (this.fsSyncTimer)
|
|
1715
|
+
return; // a sync is already scheduled in this window
|
|
1716
|
+
this.fsSyncTimer = setTimeout(() => {
|
|
1717
|
+
this.fsSyncTimer = undefined;
|
|
1718
|
+
void this.syncAndPushSessions();
|
|
1719
|
+
}, AgentWorkspaceProxy.FS_SYNC_THROTTLE_MS);
|
|
1720
|
+
}
|
|
1721
|
+
async syncAndPushSessions() {
|
|
1722
|
+
if (this.fsSyncRunning)
|
|
1723
|
+
return; // don't overlap with an in-flight sync
|
|
1724
|
+
this.fsSyncRunning = true;
|
|
1725
|
+
try {
|
|
1726
|
+
await this.syncProviderSessions();
|
|
1727
|
+
// Push the conversation list (statuses) — lighter than a full snapshot and
|
|
1728
|
+
// exactly what refreshes the tree. The web handles an unsolicited
|
|
1729
|
+
// conversation.list.result the same as a requested one.
|
|
1730
|
+
this.input.send(createEnvelope({
|
|
1731
|
+
type: "agent.v2.conversation.list.result",
|
|
1732
|
+
sessionId: this.input.sessionId,
|
|
1733
|
+
payload: { conversations: [...this.conversations.values()].filter((c) => !c.archived) },
|
|
1734
|
+
}));
|
|
1735
|
+
}
|
|
1736
|
+
catch (error) {
|
|
1737
|
+
if (this.input.verbose) {
|
|
1738
|
+
process.stderr.write(`[agent:v2] fs-watch sync failed: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
finally {
|
|
1742
|
+
this.fsSyncRunning = false;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1668
1745
|
clientForProvider(provider) {
|
|
1669
1746
|
return this.clients.get(provider);
|
|
1670
1747
|
}
|
|
@@ -1710,6 +1787,9 @@ export class AgentWorkspaceProxy {
|
|
|
1710
1787
|
this.status = "idle";
|
|
1711
1788
|
this.error = undefined;
|
|
1712
1789
|
this.sendCapabilities();
|
|
1790
|
+
// Start watching the on-disk session stores so externally-started sessions
|
|
1791
|
+
// and status changes surface live, not just at connect time.
|
|
1792
|
+
this.startSessionWatchers();
|
|
1713
1793
|
}
|
|
1714
1794
|
async ensureProviderClient(provider) {
|
|
1715
1795
|
const existing = this.clients.get(provider);
|