@linkshell/host 0.4.68 → 0.4.70
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/host/src/drivers/acp/antigravity.d.ts +13 -0
- package/dist/host/src/drivers/acp/antigravity.js +13 -0
- package/dist/host/src/drivers/acp/antigravity.js.map +1 -1
- package/dist/host/src/drivers/acp/driver.d.ts +23 -2
- package/dist/host/src/drivers/acp/driver.js +89 -5
- package/dist/host/src/drivers/acp/driver.js.map +1 -1
- package/dist/host/src/drivers/acp/mapper.d.ts +2 -0
- package/dist/host/src/drivers/acp/mapper.js +6 -1
- package/dist/host/src/drivers/acp/mapper.js.map +1 -1
- package/dist/host/src/drivers/claude/app-sessions.d.ts +58 -0
- package/dist/host/src/drivers/claude/app-sessions.js +183 -0
- package/dist/host/src/drivers/claude/app-sessions.js.map +1 -0
- package/dist/host/src/drivers/claude/driver.d.ts +37 -1
- package/dist/host/src/drivers/claude/driver.js +199 -9
- package/dist/host/src/drivers/claude/driver.js.map +1 -1
- package/dist/host/src/drivers/claude/holders.d.ts +3 -1
- package/dist/host/src/drivers/claude/holders.js +4 -0
- package/dist/host/src/drivers/claude/holders.js.map +1 -1
- package/dist/host/src/drivers/claude/routing.d.ts +19 -0
- package/dist/host/src/drivers/claude/routing.js +145 -0
- package/dist/host/src/drivers/claude/routing.js.map +1 -0
- package/dist/host/src/drivers/claude/transcript.d.ts +10 -0
- package/dist/host/src/drivers/claude/transcript.js +97 -22
- package/dist/host/src/drivers/claude/transcript.js.map +1 -1
- package/dist/host/src/drivers/codex/driver.d.ts +45 -2
- package/dist/host/src/drivers/codex/driver.js +288 -18
- package/dist/host/src/drivers/codex/driver.js.map +1 -1
- package/dist/host/src/drivers/codex/mapper.d.ts +41 -7
- package/dist/host/src/drivers/codex/mapper.js +167 -26
- package/dist/host/src/drivers/codex/mapper.js.map +1 -1
- package/dist/host/src/drivers/codex/mcp-apps.d.ts +69 -0
- package/dist/host/src/drivers/codex/mcp-apps.js +340 -0
- package/dist/host/src/drivers/codex/mcp-apps.js.map +1 -0
- package/dist/host/src/drivers/codex/settings.d.ts +13 -0
- package/dist/host/src/drivers/codex/settings.js +22 -6
- package/dist/host/src/drivers/codex/settings.js.map +1 -1
- package/dist/host/src/drivers/types.d.ts +82 -1
- package/dist/host/src/host.js +2 -1
- package/dist/host/src/host.js.map +1 -1
- package/dist/host/src/hub.d.ts +48 -1
- package/dist/host/src/hub.js +235 -13
- package/dist/host/src/hub.js.map +1 -1
- package/dist/host/src/rpc/mcp.d.ts +8 -0
- package/dist/host/src/rpc/mcp.js +33 -0
- package/dist/host/src/rpc/mcp.js.map +1 -0
- package/dist/host/src/rpc/server.js +27 -3
- package/dist/host/src/rpc/server.js.map +1 -1
- package/dist/host/src/side.d.ts +28 -0
- package/dist/host/src/side.js +142 -0
- package/dist/host/src/side.js.map +1 -0
- package/dist/host/src/store.d.ts +7 -0
- package/dist/host/src/store.js +27 -3
- package/dist/host/src/store.js.map +1 -1
- package/dist/host/src/terminals.d.ts +6 -0
- package/dist/host/src/terminals.js +29 -2
- package/dist/host/src/terminals.js.map +1 -1
- package/dist/host/src/usage.d.ts +5 -0
- package/dist/host/src/usage.js +10 -2
- package/dist/host/src/usage.js.map +1 -1
- package/dist/host/tsconfig.tsbuildinfo +1 -1
- package/dist/wire/src/envelopes.d.ts +53 -0
- package/dist/wire/src/envelopes.js +251 -0
- package/dist/wire/src/envelopes.js.map +1 -0
- package/dist/wire/src/index.d.ts +3 -0
- package/dist/wire/src/index.js +3 -0
- package/dist/wire/src/index.js.map +1 -1
- package/dist/wire/src/model.d.ts +54 -0
- package/dist/wire/src/model.js +28 -0
- package/dist/wire/src/model.js.map +1 -1
- package/dist/wire/src/rpc-mcp.d.ts +199 -0
- package/dist/wire/src/rpc-mcp.js +78 -0
- package/dist/wire/src/rpc-mcp.js.map +1 -0
- package/dist/wire/src/rpc.d.ts +823 -6
- package/dist/wire/src/rpc.js +64 -1
- package/dist/wire/src/rpc.js.map +1 -1
- package/dist/wire/src/side.d.ts +83 -0
- package/dist/wire/src/side.js +26 -0
- package/dist/wire/src/side.js.map +1 -0
- package/dist/wire/src/updates.d.ts +211 -0
- package/dist/wire/src/updates.js +14 -1
- package/dist/wire/src/updates.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../../src/host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAwC,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../../src/host.ts"],"names":[],"mappings":"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"}
|
package/dist/host/src/hub.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type GoalChange, type BackgroundTask, type AgentInfo, type ContentBlock, type SessionEvent, type SessionSummary, type SubagentInfo, type QuestionAnswer, type GitInfo, type ProjectSummary, type WorktreeEntry, type SessionNotice, type AcpAgentSettings, type AcpRemoteAgent, type PendingPermissionSummary, type EditorRequest } from "@linkshell/wire";
|
|
1
|
+
import { type GoalChange, type BackgroundTask, type AgentInfo, type ContentBlock, type SessionEvent, type SessionSummary, type SubagentInfo, type QuestionAnswer, type GitInfo, type ProjectSummary, type WorktreeEntry, type SessionNotice, type AcpAgentSettings, type AcpRemoteAgent, type PendingPermissionSummary, type EditorRequest, type SideChat, type MethodResult } from "@linkshell/wire";
|
|
2
2
|
import type { AcpSettingsStore } from "./acp-settings.js";
|
|
3
3
|
import type { AgentDriver, DesktopController, DesktopLaunchContext, DriverHost, LaunchSpec } from "./drivers/types.js";
|
|
4
4
|
import { type HostStore } from "./store.js";
|
|
@@ -9,6 +9,17 @@ export interface Subscriber {
|
|
|
9
9
|
/** The backlog that follows starts after `startSeq`, not where the subscriber left off. */
|
|
10
10
|
window?(startSeq: number): void;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Archived here or in the app, unless it was unarchived here after the app
|
|
14
|
+
* archived it (`local.at` 0: chosen before times were kept, so it never outranks the app).
|
|
15
|
+
*/
|
|
16
|
+
export declare function mergedArchive(local: {
|
|
17
|
+
archived: boolean;
|
|
18
|
+
at: number;
|
|
19
|
+
}, app: {
|
|
20
|
+
archived: boolean;
|
|
21
|
+
since: number;
|
|
22
|
+
} | undefined): boolean;
|
|
12
23
|
/**
|
|
13
24
|
* The host's source of truth for sessions. Drivers push native events in;
|
|
14
25
|
* the hub logs them with a per-session seq, keeps summaries current and fans
|
|
@@ -26,6 +37,9 @@ export declare class SessionHub {
|
|
|
26
37
|
private readonly removedListeners;
|
|
27
38
|
private readonly interactionListeners;
|
|
28
39
|
private readonly agentListeners;
|
|
40
|
+
private readonly sideListeners;
|
|
41
|
+
private readonly sides;
|
|
42
|
+
private readonly idleTimers;
|
|
29
43
|
private readonly auth;
|
|
30
44
|
/** Terminals (`linkshell <agent>`) currently driving handoff sessions. */
|
|
31
45
|
private readonly desktops;
|
|
@@ -54,6 +68,12 @@ export declare class SessionHub {
|
|
|
54
68
|
private reconcileNativeSession;
|
|
55
69
|
private hideMissingSession;
|
|
56
70
|
stop(): Promise<void>;
|
|
71
|
+
onSide(listener: (sessionId: string, side: SideChat, closed?: boolean) => void): () => void;
|
|
72
|
+
side(sessionId: string, sideId: string | undefined, text: string): SideChat;
|
|
73
|
+
listSides(sessionId: string): SideChat[];
|
|
74
|
+
cancelSide(sessionId: string, sideId: string): void;
|
|
75
|
+
closeSide(sessionId: string, sideId: string): Promise<void>;
|
|
76
|
+
private sideTarget;
|
|
57
77
|
agents(): AgentInfo[];
|
|
58
78
|
onInteraction(listener: (agent: string, request: PendingPermissionSummary | {
|
|
59
79
|
requestId: string;
|
|
@@ -306,6 +326,11 @@ export declare class SessionHub {
|
|
|
306
326
|
/** Asked for every project each time the list is: remembered for a moment. */
|
|
307
327
|
private branchOf;
|
|
308
328
|
getSession(sessionId: string): SessionSummary;
|
|
329
|
+
/** A session and its agent's driver, unchecked: for what one driver offers itself (`mcp.*`), which says what it needs of it. */
|
|
330
|
+
sessionDriver(sessionId: string): {
|
|
331
|
+
session: SessionSummary;
|
|
332
|
+
driver: AgentDriver | undefined;
|
|
333
|
+
};
|
|
309
334
|
/** Adds the live-only fields (current activity, first pending permission) to a stored summary. */
|
|
310
335
|
/** Marks a session that works in one of LinkShell's worktrees. */
|
|
311
336
|
private withWorktree;
|
|
@@ -313,7 +338,20 @@ export declare class SessionHub {
|
|
|
313
338
|
private worktrees;
|
|
314
339
|
private readonly pendingQuestions;
|
|
315
340
|
private questionsOf;
|
|
341
|
+
/** Per session, the session it was forked from (null: none), read from the store once. */
|
|
342
|
+
private readonly forkOrigins;
|
|
343
|
+
/** Per session, the sessions whose lineage names it: their rows change when it comes or goes. */
|
|
344
|
+
private readonly related;
|
|
345
|
+
private forkOrigin;
|
|
346
|
+
/** Where the session came from, naming only sessions this host has: a list can't nest under one it doesn't show. */
|
|
347
|
+
private withLineage;
|
|
348
|
+
/** As the Claude app nests: a session still in use stands alone when the one it came from is archived. */
|
|
349
|
+
private firstKnown;
|
|
350
|
+
/** The session came, went or was (un)archived: the rows that name it as parent or origin say so now. */
|
|
351
|
+
private announceRelated;
|
|
316
352
|
private decorate;
|
|
353
|
+
/** What a new session of `agent` could run on (see `agents.models`): nothing from a driver that can't tell before a session exists. */
|
|
354
|
+
agentModels(agent: string, cwd?: string): Promise<MethodResult<"agents.models">>;
|
|
317
355
|
createSession(input: {
|
|
318
356
|
agent: string;
|
|
319
357
|
cwd: string;
|
|
@@ -387,6 +425,12 @@ export declare class SessionHub {
|
|
|
387
425
|
/** What still holds at `seq` from the events before it: settings, an open turn, unanswered approvals. */
|
|
388
426
|
private standingBefore;
|
|
389
427
|
unsubscribe(sessionId: string, subscriber: Subscriber): void;
|
|
428
|
+
/**
|
|
429
|
+
* Detaches a session nobody has open, from a driver that pays to follow it, once it has been
|
|
430
|
+
* left alone a while with nothing under way. Without this every session opened since the host
|
|
431
|
+
* started stays followed for good.
|
|
432
|
+
*/
|
|
433
|
+
private detachWhenIdle;
|
|
390
434
|
prompt(sessionId: string, clientMessageId: string, content: ContentBlock[], whenBusy?: "queue"): Promise<"started" | "steered" | "queued" | "duplicate">;
|
|
391
435
|
/** Retries attaching a session that just became joinable. */
|
|
392
436
|
private attachWhenReady;
|
|
@@ -401,6 +445,9 @@ export declare class SessionHub {
|
|
|
401
445
|
private sendHeld;
|
|
402
446
|
onRemoved(listener: (sessionId: string) => void): () => void;
|
|
403
447
|
archive(sessionId: string, archived: boolean): Promise<SessionSummary>;
|
|
448
|
+
/** Folds in the archive state of the agent's app that LinkShell only reads (see `mergedArchive`). */
|
|
449
|
+
private reconcileAppArchive;
|
|
450
|
+
private localArchive;
|
|
404
451
|
rename(sessionId: string, title: string): Promise<SessionSummary>;
|
|
405
452
|
/**
|
|
406
453
|
* Deletes a session. A worktree only it used goes too when nothing in it
|
package/dist/host/src/hub.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RpcError, questionReplies, questionAnswerError, updateAsyncQuestions, sessionIdFor, } from "@linkshell/wire";
|
|
1
|
+
import { RpcError, questionReplies, readUserText, questionAnswerError, updateAsyncQuestions, sessionIdFor, } from "@linkshell/wire";
|
|
2
2
|
import { inOrder } from "./drivers/acp/driver.js";
|
|
3
3
|
import { canForkNatively } from "./drivers/behavior.js";
|
|
4
4
|
import { homedir } from "node:os";
|
|
@@ -10,6 +10,7 @@ import { slimEvent } from "./slim.js";
|
|
|
10
10
|
import { outputRange } from "./task-output.js";
|
|
11
11
|
import { ComputerPreviews } from "./computer-preview.js";
|
|
12
12
|
import { MacPreviewCapture } from "./computer-preview-capture.js";
|
|
13
|
+
import { SideChats } from "./side.js";
|
|
13
14
|
/**
|
|
14
15
|
* Whether an update is something happening in the session. Names, settings
|
|
15
16
|
* and usage figures arrive when a session is merely opened or renamed, and
|
|
@@ -56,6 +57,24 @@ function worktreeLossMessage(state) {
|
|
|
56
57
|
const RENAMED = "renamed";
|
|
57
58
|
/** Driver-state key: the conversation a forked session's agent is still to be told, with its first message. */
|
|
58
59
|
const CARRY = "carry";
|
|
60
|
+
/** Driver-state key: the session a session was forked from (made here, or as its agent recorded it). */
|
|
61
|
+
const FORKED_FROM = "forkedFrom";
|
|
62
|
+
/**
|
|
63
|
+
* Driver-state keys, for agents with an app LinkShell only reads (`appArchive`):
|
|
64
|
+
* LinkShell's own archive choice and when it was made, and the app's state as
|
|
65
|
+
* last seen and since when.
|
|
66
|
+
*/
|
|
67
|
+
const LOCAL_ARCHIVE = "archive-local";
|
|
68
|
+
const APP_ARCHIVE = "archive-app";
|
|
69
|
+
/**
|
|
70
|
+
* Archived here or in the app, unless it was unarchived here after the app
|
|
71
|
+
* archived it (`local.at` 0: chosen before times were kept, so it never outranks the app).
|
|
72
|
+
*/
|
|
73
|
+
export function mergedArchive(local, app) {
|
|
74
|
+
if (local.archived)
|
|
75
|
+
return true;
|
|
76
|
+
return !!app?.archived && !(local.at > app.since);
|
|
77
|
+
}
|
|
59
78
|
/** A returning client catches up from where it was, unless it missed more than this. */
|
|
60
79
|
const CATCH_UP = { events: 800, bytes: 4 * 1024 * 1024 };
|
|
61
80
|
/** State a client needs from before its window: the latest of each applies. */
|
|
@@ -68,6 +87,8 @@ const STANDING = [
|
|
|
68
87
|
"session_info_update",
|
|
69
88
|
"ls_driver",
|
|
70
89
|
];
|
|
90
|
+
/** How long a session nobody has open stays attached to a driver that pays to follow it. */
|
|
91
|
+
const IDLE_DETACH_MS = 10 * 60_000;
|
|
71
92
|
const PREVIEW_LENGTH = 160;
|
|
72
93
|
const TITLE_LENGTH = 60;
|
|
73
94
|
function compact(text, max) {
|
|
@@ -99,6 +120,9 @@ export class SessionHub {
|
|
|
99
120
|
removedListeners = new Set();
|
|
100
121
|
interactionListeners = new Set();
|
|
101
122
|
agentListeners = new Set();
|
|
123
|
+
sideListeners = new Set();
|
|
124
|
+
sides;
|
|
125
|
+
idleTimers = new Map();
|
|
102
126
|
auth = new Map();
|
|
103
127
|
/** Terminals (`linkshell <agent>`) currently driving handoff sessions. */
|
|
104
128
|
desktops = new Map();
|
|
@@ -166,6 +190,17 @@ export class SessionHub {
|
|
|
166
190
|
this.emitSummary(stored);
|
|
167
191
|
},
|
|
168
192
|
removed: (agent, nativeId) => this.forget(sessionIdFor(agent, nativeId)),
|
|
193
|
+
nativeRecordsChanged: (agent, nativeIds) => {
|
|
194
|
+
for (const nativeId of nativeIds) {
|
|
195
|
+
const stored = this.store.getSession(sessionIdFor(agent, nativeId));
|
|
196
|
+
if (!stored)
|
|
197
|
+
continue;
|
|
198
|
+
const merged = this.reconcileAppArchive(stored);
|
|
199
|
+
this.emitSummary(merged);
|
|
200
|
+
if (merged.archived !== stored.archived)
|
|
201
|
+
this.announceRelated(merged.id);
|
|
202
|
+
}
|
|
203
|
+
},
|
|
169
204
|
desktop: (agent, nativeId) => this.desktops.get(sessionIdFor(agent, nativeId)),
|
|
170
205
|
state: (agent, nativeId) => {
|
|
171
206
|
const sessionId = sessionIdFor(agent, nativeId);
|
|
@@ -174,6 +209,10 @@ export class SessionHub {
|
|
|
174
209
|
set: (key, value) => this.store.setDriverState(sessionId, key, value),
|
|
175
210
|
};
|
|
176
211
|
},
|
|
212
|
+
agentState: (agent) => ({
|
|
213
|
+
get: (key) => this.store.getAgentState(agent, key),
|
|
214
|
+
set: (key, value) => this.store.setAgentState(agent, key, value),
|
|
215
|
+
}),
|
|
177
216
|
log: (message) => this.log(message),
|
|
178
217
|
};
|
|
179
218
|
constructor(store, drivers, log = () => { },
|
|
@@ -195,6 +234,10 @@ export class SessionHub {
|
|
|
195
234
|
});
|
|
196
235
|
for (const driver of drivers)
|
|
197
236
|
this.drivers.set(driver.id, driver);
|
|
237
|
+
this.sides = new SideChats((sessionId, side, closed) => {
|
|
238
|
+
for (const listener of this.sideListeners)
|
|
239
|
+
listener(sessionId, side, closed);
|
|
240
|
+
}, log);
|
|
198
241
|
}
|
|
199
242
|
async start(options = {}) {
|
|
200
243
|
for (const session of this.store.errorSessions()) {
|
|
@@ -281,6 +324,7 @@ export class SessionHub {
|
|
|
281
324
|
else if (availability === "available" && this.store.isNativeMissing(session.id)) {
|
|
282
325
|
this.store.setNativeMissing(session.id, false);
|
|
283
326
|
this.emitSummary(session);
|
|
327
|
+
this.announceRelated(session.id);
|
|
284
328
|
}
|
|
285
329
|
}
|
|
286
330
|
hideMissingSession(session) {
|
|
@@ -295,14 +339,44 @@ export class SessionHub {
|
|
|
295
339
|
driver?.watched?.(session.nativeId, false);
|
|
296
340
|
this.live.delete(session.id);
|
|
297
341
|
this.emitRemoved(session.id);
|
|
342
|
+
this.announceRelated(session.id);
|
|
298
343
|
void driver?.detach(session.nativeId).catch((error) => this.log(`[hub] detach missing ${session.id}: ${String(error)}`));
|
|
299
344
|
}
|
|
300
345
|
async stop() {
|
|
301
346
|
if (this.discoveryTimer)
|
|
302
347
|
clearInterval(this.discoveryTimer);
|
|
348
|
+
for (const timer of this.idleTimers.values())
|
|
349
|
+
clearTimeout(timer);
|
|
350
|
+
this.idleTimers.clear();
|
|
351
|
+
await this.sides.stop((sessionId) => this.sideTarget(sessionId));
|
|
303
352
|
await Promise.allSettled([...this.drivers.values()].map((driver) => driver.stop()));
|
|
304
353
|
await this.previews.stop();
|
|
305
354
|
}
|
|
355
|
+
// ── side conversations ────────────────────────────────────────────
|
|
356
|
+
onSide(listener) {
|
|
357
|
+
this.sideListeners.add(listener);
|
|
358
|
+
return () => this.sideListeners.delete(listener);
|
|
359
|
+
}
|
|
360
|
+
side(sessionId, sideId, text) {
|
|
361
|
+
const summary = this.getSession(sessionId);
|
|
362
|
+
return this.sides.ask(sessionId, { driver: this.requireDriver(summary.agent), nativeId: summary.nativeId, cwd: summary.cwd }, sideId, text);
|
|
363
|
+
}
|
|
364
|
+
listSides(sessionId) {
|
|
365
|
+
this.getSession(sessionId);
|
|
366
|
+
return this.sides.list(sessionId);
|
|
367
|
+
}
|
|
368
|
+
cancelSide(sessionId, sideId) {
|
|
369
|
+
this.sides.cancel(sessionId, sideId);
|
|
370
|
+
}
|
|
371
|
+
async closeSide(sessionId, sideId) {
|
|
372
|
+
const target = this.sideTarget(sessionId);
|
|
373
|
+
await this.sides.close(sessionId, sideId, target?.driver, target?.nativeId ?? "");
|
|
374
|
+
}
|
|
375
|
+
sideTarget(sessionId) {
|
|
376
|
+
const summary = this.store.getSession(sessionId);
|
|
377
|
+
const driver = summary && this.drivers.get(summary.agent);
|
|
378
|
+
return summary && driver ? { driver, nativeId: summary.nativeId } : undefined;
|
|
379
|
+
}
|
|
306
380
|
agents() {
|
|
307
381
|
return [...this.drivers.values()].map((driver) => {
|
|
308
382
|
const status = driver.status();
|
|
@@ -386,6 +460,8 @@ export class SessionHub {
|
|
|
386
460
|
await existing.configureAcp?.(services.settings.settings(id));
|
|
387
461
|
await existing.stop();
|
|
388
462
|
this.drivers.delete(id);
|
|
463
|
+
// What the old definition's agent reported (its models) needn't hold for the new one.
|
|
464
|
+
this.store.clearAgentState(id);
|
|
389
465
|
}
|
|
390
466
|
if (save && driver) {
|
|
391
467
|
services.settings.saveAgent(save);
|
|
@@ -454,6 +530,11 @@ export class SessionHub {
|
|
|
454
530
|
throw RpcError.app("not_found", "这个会话的原始记录已不存在,已从列表隐藏;本地缓存仍保留。");
|
|
455
531
|
return this.decorate(summary);
|
|
456
532
|
}
|
|
533
|
+
/** A session and its agent's driver, unchecked: for what one driver offers itself (`mcp.*`), which says what it needs of it. */
|
|
534
|
+
sessionDriver(sessionId) {
|
|
535
|
+
const session = this.getSession(sessionId);
|
|
536
|
+
return { session, driver: this.drivers.get(session.agent) };
|
|
537
|
+
}
|
|
457
538
|
/** Adds the live-only fields (current activity, first pending permission) to a stored summary. */
|
|
458
539
|
/** Marks a session that works in one of LinkShell's worktrees. */
|
|
459
540
|
withWorktree(summary) {
|
|
@@ -473,8 +554,55 @@ export class SessionHub {
|
|
|
473
554
|
}
|
|
474
555
|
return questions;
|
|
475
556
|
}
|
|
557
|
+
/** Per session, the session it was forked from (null: none), read from the store once. */
|
|
558
|
+
forkOrigins = new Map();
|
|
559
|
+
/** Per session, the sessions whose lineage names it: their rows change when it comes or goes. */
|
|
560
|
+
related = new Map();
|
|
561
|
+
forkOrigin(sessionId) {
|
|
562
|
+
let origin = this.forkOrigins.get(sessionId);
|
|
563
|
+
if (origin === undefined) {
|
|
564
|
+
origin = this.store.getDriverState(sessionId, FORKED_FROM) ?? null;
|
|
565
|
+
this.forkOrigins.set(sessionId, origin);
|
|
566
|
+
}
|
|
567
|
+
return origin ?? undefined;
|
|
568
|
+
}
|
|
569
|
+
/** Where the session came from, naming only sessions this host has: a list can't nest under one it doesn't show. */
|
|
570
|
+
withLineage(summary) {
|
|
571
|
+
const native = this.drivers.get(summary.agent)?.lineage?.(summary.nativeId);
|
|
572
|
+
const ids = (nativeIds) => nativeIds?.map((nativeId) => sessionIdFor(summary.agent, nativeId)) ?? [];
|
|
573
|
+
const origin = this.forkOrigin(summary.id);
|
|
574
|
+
const parentId = this.firstKnown(summary, ids(native?.parent));
|
|
575
|
+
const forkedFrom = this.firstKnown(summary, [...(origin ? [origin] : []), ...ids(native?.forkedFrom)]);
|
|
576
|
+
const appId = native?.appId;
|
|
577
|
+
if (!parentId && !forkedFrom && !appId)
|
|
578
|
+
return summary;
|
|
579
|
+
return { ...summary, ...(parentId ? { parentId } : {}), ...(forkedFrom ? { forkedFrom } : {}), ...(appId ? { appId } : {}) };
|
|
580
|
+
}
|
|
581
|
+
/** As the Claude app nests: a session still in use stands alone when the one it came from is archived. */
|
|
582
|
+
firstKnown(summary, candidates) {
|
|
583
|
+
let found;
|
|
584
|
+
for (const candidate of candidates) {
|
|
585
|
+
if (candidate === summary.id)
|
|
586
|
+
continue;
|
|
587
|
+
let children = this.related.get(candidate);
|
|
588
|
+
if (!children)
|
|
589
|
+
this.related.set(candidate, (children = new Set()));
|
|
590
|
+
children.add(summary.id);
|
|
591
|
+
if (found)
|
|
592
|
+
continue;
|
|
593
|
+
const other = this.store.getSession(candidate);
|
|
594
|
+
if (other && !(other.archived && !summary.archived) && !this.store.isNativeMissing(candidate))
|
|
595
|
+
found = candidate;
|
|
596
|
+
}
|
|
597
|
+
return found;
|
|
598
|
+
}
|
|
599
|
+
/** The session came, went or was (un)archived: the rows that name it as parent or origin say so now. */
|
|
600
|
+
announceRelated(sessionId) {
|
|
601
|
+
for (const child of this.related.get(sessionId) ?? [])
|
|
602
|
+
this.announce(child);
|
|
603
|
+
}
|
|
476
604
|
decorate(summary) {
|
|
477
|
-
summary = { ...summary, asyncQuestions: this.questionsOf(summary.id) };
|
|
605
|
+
summary = this.withLineage({ ...summary, asyncQuestions: this.questionsOf(summary.id) });
|
|
478
606
|
const live = this.live.get(summary.id);
|
|
479
607
|
if (!live)
|
|
480
608
|
return this.withWorktree(summary);
|
|
@@ -522,6 +650,11 @@ export class SessionHub {
|
|
|
522
650
|
}
|
|
523
651
|
return decorated;
|
|
524
652
|
}
|
|
653
|
+
/** What a new session of `agent` could run on (see `agents.models`): nothing from a driver that can't tell before a session exists. */
|
|
654
|
+
async agentModels(agent, cwd) {
|
|
655
|
+
const driver = this.requireDriver(agent);
|
|
656
|
+
return (await driver.listModels?.(cwd)) ?? { models: [] };
|
|
657
|
+
}
|
|
525
658
|
async createSession(input) {
|
|
526
659
|
const driver = this.requireDriver(input.agent);
|
|
527
660
|
const worktree = input.worktree ? await this.newWorktree(input.cwd, promptLabel(input.prompt)) : undefined;
|
|
@@ -576,7 +709,7 @@ export class SessionHub {
|
|
|
576
709
|
}
|
|
577
710
|
if (!discovered)
|
|
578
711
|
return await this.forkByReplay(summary, driver, cwd, cut);
|
|
579
|
-
this.recordDiscovered(driver.id, { ...discovered, title: discovered.title ?? summary.title });
|
|
712
|
+
this.recordDiscovered(driver.id, { ...discovered, title: discovered.title ?? summary.title, forkedFrom: summary.nativeId });
|
|
580
713
|
return this.getSession(sessionIdFor(driver.id, discovered.nativeId));
|
|
581
714
|
}
|
|
582
715
|
catch (error) {
|
|
@@ -594,7 +727,7 @@ export class SessionHub {
|
|
|
594
727
|
const start = this.store.pageStart(summary.id, cut);
|
|
595
728
|
const events = this.store.readEvents(summary.id, start, PAGE.maxEvents + 1, cut).filter((event) => isConversation(event.update));
|
|
596
729
|
const discovered = await driver.createSession({ cwd });
|
|
597
|
-
this.recordDiscovered(driver.id, { ...discovered, title: discovered.title ?? summary.title });
|
|
730
|
+
this.recordDiscovered(driver.id, { ...discovered, title: discovered.title ?? summary.title, forkedFrom: summary.nativeId });
|
|
598
731
|
const forkId = sessionIdFor(driver.id, discovered.nativeId);
|
|
599
732
|
await this.ensureAttached(forkId);
|
|
600
733
|
const live = this.liveFor(forkId);
|
|
@@ -731,6 +864,8 @@ export class SessionHub {
|
|
|
731
864
|
cursor = batch[batch.length - 1].seq;
|
|
732
865
|
}
|
|
733
866
|
live.subscribers.add(subscriber);
|
|
867
|
+
clearTimeout(this.idleTimers.get(sessionId));
|
|
868
|
+
this.idleTimers.delete(sessionId);
|
|
734
869
|
if (live.subscribers.size === 1)
|
|
735
870
|
driver?.watched?.(summary.nativeId, true);
|
|
736
871
|
return { session: this.getSession(sessionId), startSeq };
|
|
@@ -962,6 +1097,38 @@ export class SessionHub {
|
|
|
962
1097
|
const summary = this.store.getSession(sessionId);
|
|
963
1098
|
if (summary)
|
|
964
1099
|
this.drivers.get(summary.agent)?.watched?.(summary.nativeId, false);
|
|
1100
|
+
this.detachWhenIdle(sessionId);
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Detaches a session nobody has open, from a driver that pays to follow it, once it has been
|
|
1104
|
+
* left alone a while with nothing under way. Without this every session opened since the host
|
|
1105
|
+
* started stays followed for good.
|
|
1106
|
+
*/
|
|
1107
|
+
detachWhenIdle(sessionId, delay = IDLE_DETACH_MS) {
|
|
1108
|
+
const summary = this.store.getSession(sessionId);
|
|
1109
|
+
const driver = summary && this.drivers.get(summary.agent);
|
|
1110
|
+
if (!summary || !driver?.detachWhenIdle)
|
|
1111
|
+
return;
|
|
1112
|
+
clearTimeout(this.idleTimers.get(sessionId));
|
|
1113
|
+
const timer = setTimeout(() => {
|
|
1114
|
+
this.idleTimers.delete(sessionId);
|
|
1115
|
+
const live = this.live.get(sessionId);
|
|
1116
|
+
if (!live || live.subscribers.size > 0 || !live.attached || live.attaching || live.detaching)
|
|
1117
|
+
return;
|
|
1118
|
+
const tasks = this.tasksOf(sessionId, live);
|
|
1119
|
+
const working = live.turnActive || live.permissions.size > 0 || !!live.queue?.length || live.held.length > 0
|
|
1120
|
+
|| [...tasks.values()].some((task) => task.state === "running")
|
|
1121
|
+
|| [...(live.subagents?.values() ?? [])].some((state) => subagentRunning(state));
|
|
1122
|
+
// Something is under way: looked at again later.
|
|
1123
|
+
if (working)
|
|
1124
|
+
return this.detachWhenIdle(sessionId, delay);
|
|
1125
|
+
live.attached = false;
|
|
1126
|
+
live.detaching = driver.detach(summary.nativeId)
|
|
1127
|
+
.catch((error) => this.log(`[hub] idle detach ${sessionId}: ${String(error)}`))
|
|
1128
|
+
.finally(() => { live.detaching = undefined; });
|
|
1129
|
+
}, delay);
|
|
1130
|
+
timer.unref?.();
|
|
1131
|
+
this.idleTimers.set(sessionId, timer);
|
|
965
1132
|
}
|
|
966
1133
|
async prompt(sessionId, clientMessageId, content, whenBusy) {
|
|
967
1134
|
const summary = this.getSession(sessionId);
|
|
@@ -1112,10 +1279,40 @@ export class SessionHub {
|
|
|
1112
1279
|
const driver = this.drivers.get(summary.agent);
|
|
1113
1280
|
if (driver?.archive)
|
|
1114
1281
|
await driver.archive(summary.nativeId, archived);
|
|
1115
|
-
|
|
1282
|
+
// Kept apart from the app's, with when: unarchiving here outranks an older archive there.
|
|
1283
|
+
if (driver?.appArchive)
|
|
1284
|
+
this.store.setDriverState(sessionId, LOCAL_ARCHIVE, JSON.stringify({ archived, at: Date.now() }));
|
|
1285
|
+
const updated = this.reconcileAppArchive(this.store.patchSession(sessionId, { archived }));
|
|
1116
1286
|
this.emitSummary(updated);
|
|
1287
|
+
this.announceRelated(sessionId);
|
|
1117
1288
|
return this.decorate(updated);
|
|
1118
1289
|
}
|
|
1290
|
+
/** Folds in the archive state of the agent's app that LinkShell only reads (see `mergedArchive`). */
|
|
1291
|
+
reconcileAppArchive(summary) {
|
|
1292
|
+
const reported = this.drivers.get(summary.agent)?.appArchive?.(summary.nativeId);
|
|
1293
|
+
// Not in that app (or not read yet): the flag is LinkShell's alone, or stays as last merged.
|
|
1294
|
+
if (!reported)
|
|
1295
|
+
return summary;
|
|
1296
|
+
const saved = this.store.getDriverState(summary.id, APP_ARCHIVE);
|
|
1297
|
+
const seen = saved ? JSON.parse(saved) : undefined;
|
|
1298
|
+
if (!seen && !reported.archived)
|
|
1299
|
+
return summary;
|
|
1300
|
+
// Only a change counts as the app archiving it: the app rewrites its record for much else.
|
|
1301
|
+
const app = seen?.archived === reported.archived ? seen : { archived: reported.archived, since: reported.at };
|
|
1302
|
+
if (app !== seen)
|
|
1303
|
+
this.store.setDriverState(summary.id, APP_ARCHIVE, JSON.stringify(app));
|
|
1304
|
+
const archived = mergedArchive(this.localArchive(summary), app);
|
|
1305
|
+
return archived === summary.archived ? summary : this.store.patchSession(summary.id, { archived });
|
|
1306
|
+
}
|
|
1307
|
+
localArchive(summary) {
|
|
1308
|
+
const saved = this.store.getDriverState(summary.id, LOCAL_ARCHIVE);
|
|
1309
|
+
if (saved)
|
|
1310
|
+
return JSON.parse(saved);
|
|
1311
|
+
// Until the app's state is first merged in, the flag is LinkShell's own choice.
|
|
1312
|
+
const local = { archived: summary.archived, at: 0 };
|
|
1313
|
+
this.store.setDriverState(summary.id, LOCAL_ARCHIVE, JSON.stringify(local));
|
|
1314
|
+
return local;
|
|
1315
|
+
}
|
|
1119
1316
|
async rename(sessionId, title) {
|
|
1120
1317
|
const summary = this.getSession(sessionId);
|
|
1121
1318
|
const name = compact(title, TITLE_LENGTH);
|
|
@@ -1170,10 +1367,15 @@ export class SessionHub {
|
|
|
1170
1367
|
forget(sessionId) {
|
|
1171
1368
|
if (!this.store.getSession(sessionId))
|
|
1172
1369
|
return;
|
|
1370
|
+
clearTimeout(this.idleTimers.get(sessionId));
|
|
1371
|
+
this.idleTimers.delete(sessionId);
|
|
1173
1372
|
this.live.delete(sessionId);
|
|
1174
1373
|
this.previews.forget(sessionId);
|
|
1374
|
+
this.sides.forget(sessionId);
|
|
1175
1375
|
this.store.removeSession(sessionId);
|
|
1376
|
+
this.forkOrigins.delete(sessionId);
|
|
1176
1377
|
this.emitRemoved(sessionId);
|
|
1378
|
+
this.announceRelated(sessionId);
|
|
1177
1379
|
}
|
|
1178
1380
|
emitRemoved(sessionId) {
|
|
1179
1381
|
for (const listener of this.removedListeners) {
|
|
@@ -1328,6 +1530,8 @@ export class SessionHub {
|
|
|
1328
1530
|
/** Never throws synchronously: failures always arrive as a rejected promise. */
|
|
1329
1531
|
ensureAttached(sessionId) {
|
|
1330
1532
|
const live = this.liveFor(sessionId);
|
|
1533
|
+
if (live.detaching)
|
|
1534
|
+
return live.detaching.then(() => this.ensureAttached(sessionId));
|
|
1331
1535
|
if (live.attached)
|
|
1332
1536
|
return Promise.resolve();
|
|
1333
1537
|
if (live.attaching)
|
|
@@ -1353,10 +1557,19 @@ export class SessionHub {
|
|
|
1353
1557
|
// Items without a recorded time get the time of the one before, never "now".
|
|
1354
1558
|
// (An agent that only says when a session last changed gives that as its creation too.)
|
|
1355
1559
|
let ts = Math.min(summary.createdAt, history.find((item) => item.ts !== undefined)?.ts ?? summary.createdAt);
|
|
1356
|
-
|
|
1560
|
+
// The log only grows at its end. A message before what it already has is one an earlier
|
|
1561
|
+
// reading skipped (a reader that now shows more), not something that happened since:
|
|
1562
|
+
// added now it would land at the bottom, out of place, so it is only marked as seen.
|
|
1563
|
+
const logged = history.map((item) => this.store.isItemLogged(sessionId, item.itemId));
|
|
1564
|
+
const lastLogged = logged.lastIndexOf(true);
|
|
1565
|
+
for (const [index, item] of history.entries()) {
|
|
1357
1566
|
ts = Math.min(Math.max(item.ts ?? ts, ts), summary.updatedAt);
|
|
1358
|
-
if (
|
|
1567
|
+
if (logged[index])
|
|
1568
|
+
continue;
|
|
1569
|
+
if (index < lastLogged && item.updates.every((update) => update.sessionUpdate === "user_message_chunk")) {
|
|
1570
|
+
this.store.markItemLogged(sessionId, item.itemId);
|
|
1359
1571
|
continue;
|
|
1572
|
+
}
|
|
1360
1573
|
for (const update of item.updates)
|
|
1361
1574
|
this.commit(sessionId, update, undefined, ts);
|
|
1362
1575
|
this.store.markItemLogged(sessionId, item.itemId);
|
|
@@ -1422,9 +1635,9 @@ export class SessionHub {
|
|
|
1422
1635
|
updatedAt: session.updatedAt,
|
|
1423
1636
|
});
|
|
1424
1637
|
const { created } = stored;
|
|
1425
|
-
const summary = session.archived !== undefined && session.archived !== stored.summary.archived
|
|
1638
|
+
const summary = this.reconcileAppArchive(session.archived !== undefined && session.archived !== stored.summary.archived
|
|
1426
1639
|
? this.store.patchSession(stored.summary.id, { archived: session.archived })
|
|
1427
|
-
: stored.summary;
|
|
1640
|
+
: stored.summary);
|
|
1428
1641
|
const changed = !!before &&
|
|
1429
1642
|
(before.title !== summary.title ||
|
|
1430
1643
|
before.state !== summary.state ||
|
|
@@ -1439,8 +1652,16 @@ export class SessionHub {
|
|
|
1439
1652
|
if (questionsChanged)
|
|
1440
1653
|
this.store.setDriverState(summary.id, "asyncQuestions", JSON.stringify(session.asyncQuestions));
|
|
1441
1654
|
}
|
|
1442
|
-
|
|
1655
|
+
const origin = session.forkedFrom === undefined ? undefined : sessionIdFor(agent, session.forkedFrom);
|
|
1656
|
+
const forked = origin !== undefined && origin !== summary.id && origin !== this.forkOrigin(summary.id);
|
|
1657
|
+
if (forked) {
|
|
1658
|
+
this.store.setDriverState(summary.id, FORKED_FROM, origin);
|
|
1659
|
+
this.forkOrigins.set(summary.id, origin);
|
|
1660
|
+
}
|
|
1661
|
+
if (created || changed || questionsChanged || restored || forked)
|
|
1443
1662
|
this.emitSummary(summary);
|
|
1663
|
+
if (created || restored || before?.archived !== summary.archived)
|
|
1664
|
+
this.announceRelated(summary.id);
|
|
1444
1665
|
}
|
|
1445
1666
|
ingest(sessionId, update, itemId, ts) {
|
|
1446
1667
|
if (this.store.isNativeMissing(sessionId))
|
|
@@ -1517,8 +1738,9 @@ export class SessionHub {
|
|
|
1517
1738
|
if (update.parentToolCallId || !update.content)
|
|
1518
1739
|
break;
|
|
1519
1740
|
const text = update.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
1520
|
-
|
|
1521
|
-
|
|
1741
|
+
// A title is the person's words, not the context their client or Claude wrapped around them.
|
|
1742
|
+
if (update.role === "user" && !before.title && readUserText(text).text.trim())
|
|
1743
|
+
patch.title = compact(readUserText(text).text, TITLE_LENGTH);
|
|
1522
1744
|
if (update.role === "agent" && text.trim())
|
|
1523
1745
|
patch.preview = compact(text, PREVIEW_LENGTH);
|
|
1524
1746
|
if (live.turnActive && update.role === "agent")
|
|
@@ -1526,7 +1748,7 @@ export class SessionHub {
|
|
|
1526
1748
|
break;
|
|
1527
1749
|
}
|
|
1528
1750
|
case "user_message_chunk": {
|
|
1529
|
-
const text = update.content.type === "text" ? update.content.text : "";
|
|
1751
|
+
const text = update.content.type === "text" ? readUserText(update.content.text).text : "";
|
|
1530
1752
|
if (text && !before.title)
|
|
1531
1753
|
patch.title = compact(text, TITLE_LENGTH);
|
|
1532
1754
|
if (text && !before.preview)
|