linkshell-cli 0.10.19 → 0.10.20
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/README.md +3 -2
- package/dist/cli/src/commands/screen.js +2 -2
- package/dist/cli/src/commands/screen.js.map +1 -1
- package/dist/cli/tsconfig.tsbuildinfo +1 -1
- package/dist/host/src/acp-settings.d.ts +12 -0
- package/dist/host/src/acp-settings.js +41 -0
- package/dist/host/src/acp-settings.js.map +1 -0
- package/dist/host/src/carry.js +13 -2
- package/dist/host/src/carry.js.map +1 -1
- package/dist/host/src/drivers/acp/client-services.d.ts +29 -0
- package/dist/host/src/drivers/acp/client-services.js +192 -0
- package/dist/host/src/drivers/acp/client-services.js.map +1 -0
- package/dist/host/src/drivers/acp/connection.d.ts +37 -8
- package/dist/host/src/drivers/acp/connection.js +127 -110
- package/dist/host/src/drivers/acp/connection.js.map +1 -1
- package/dist/host/src/drivers/acp/driver.d.ts +92 -2
- package/dist/host/src/drivers/acp/driver.js +599 -52
- package/dist/host/src/drivers/acp/driver.js.map +1 -1
- package/dist/host/src/drivers/acp/editor.d.ts +31 -0
- package/dist/host/src/drivers/acp/editor.js +242 -0
- package/dist/host/src/drivers/acp/editor.js.map +1 -0
- package/dist/host/src/drivers/acp/mapper.d.ts +2 -1
- package/dist/host/src/drivers/acp/mapper.js +88 -35
- package/dist/host/src/drivers/acp/mapper.js.map +1 -1
- package/dist/host/src/drivers/acp/mcp.d.ts +20 -0
- package/dist/host/src/drivers/acp/mcp.js +139 -0
- package/dist/host/src/drivers/acp/mcp.js.map +1 -0
- package/dist/host/src/drivers/acp/questions.js +14 -0
- package/dist/host/src/drivers/acp/questions.js.map +1 -1
- package/dist/host/src/drivers/acp/updates.d.ts +10 -0
- package/dist/host/src/drivers/acp/updates.js +137 -0
- package/dist/host/src/drivers/acp/updates.js.map +1 -0
- package/dist/host/src/drivers/claude/activity.d.ts +1 -0
- package/dist/host/src/drivers/claude/activity.js +17 -3
- package/dist/host/src/drivers/claude/activity.js.map +1 -1
- package/dist/host/src/drivers/claude/children.d.ts +7 -0
- package/dist/host/src/drivers/claude/children.js +31 -2
- package/dist/host/src/drivers/claude/children.js.map +1 -1
- package/dist/host/src/drivers/claude/driver.js +1 -0
- package/dist/host/src/drivers/claude/driver.js.map +1 -1
- package/dist/host/src/drivers/claude/tasks.d.ts +3 -3
- package/dist/host/src/drivers/claude/transcript.d.ts +497 -38
- package/dist/host/src/drivers/codex/driver.d.ts +3 -0
- package/dist/host/src/drivers/codex/driver.js +36 -1
- package/dist/host/src/drivers/codex/driver.js.map +1 -1
- package/dist/host/src/drivers/registry.d.ts +6 -0
- package/dist/host/src/drivers/registry.js +8 -0
- package/dist/host/src/drivers/registry.js.map +1 -1
- package/dist/host/src/drivers/types.d.ts +34 -1
- package/dist/host/src/fs.d.ts +3 -3
- package/dist/host/src/host.js +21 -3
- package/dist/host/src/host.js.map +1 -1
- package/dist/host/src/hub.d.ts +249 -2
- package/dist/host/src/hub.js +150 -9
- package/dist/host/src/hub.js.map +1 -1
- package/dist/host/src/input.d.ts +5 -3
- package/dist/host/src/input.js +8 -6
- package/dist/host/src/input.js.map +1 -1
- package/dist/host/src/questions.d.ts +3 -1
- package/dist/host/src/questions.js +33 -7
- package/dist/host/src/questions.js.map +1 -1
- package/dist/host/src/rpc/server.js +14 -0
- package/dist/host/src/rpc/server.js.map +1 -1
- package/dist/host/src/screen.js +5 -5
- package/dist/host/src/screen.js.map +1 -1
- package/dist/host/src/slim.js +15 -4
- package/dist/host/src/slim.js.map +1 -1
- package/dist/host/src/store.js +7 -2
- package/dist/host/src/store.js.map +1 -1
- package/dist/host/src/terminals.d.ts +8 -3
- package/dist/host/src/terminals.js +3 -2
- package/dist/host/src/terminals.js.map +1 -1
- package/dist/wire/src/acp.d.ts +510 -0
- package/dist/wire/src/acp.js +39 -0
- package/dist/wire/src/acp.js.map +1 -0
- package/dist/wire/src/async-questions.d.ts +2 -2
- package/dist/wire/src/computer-preview.d.ts +2 -2
- package/dist/wire/src/content.d.ts +465 -0
- package/dist/wire/src/content.js +58 -0
- package/dist/wire/src/content.js.map +1 -0
- package/dist/wire/src/editor.d.ts +702 -0
- package/dist/wire/src/editor.js +23 -0
- package/dist/wire/src/editor.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 +1383 -79
- package/dist/wire/src/model.js +20 -1
- package/dist/wire/src/model.js.map +1 -1
- package/dist/wire/src/peer.d.ts +1 -0
- package/dist/wire/src/peer.js +22 -6
- package/dist/wire/src/peer.js.map +1 -1
- package/dist/wire/src/questions.d.ts +3 -0
- package/dist/wire/src/questions.js +65 -0
- package/dist/wire/src/questions.js.map +1 -0
- package/dist/wire/src/relay.d.ts +4 -4
- package/dist/wire/src/rpc.d.ts +19023 -2518
- package/dist/wire/src/rpc.js +41 -3
- package/dist/wire/src/rpc.js.map +1 -1
- package/dist/wire/src/updates.d.ts +3848 -1114
- package/dist/wire/src/updates.js +42 -40
- package/dist/wire/src/updates.js.map +1 -1
- package/dist/wire/src/workflow.d.ts +22 -22
- package/package.json +4 -4
- package/src/commands/screen.ts +2 -2
package/dist/host/src/host.js
CHANGED
|
@@ -3,7 +3,8 @@ import { homedir, hostname, platform } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
5
|
import { DEFAULT_ICE_SERVERS } from "@linkshell/wire";
|
|
6
|
-
import { defaultDrivers } from "./drivers/registry.js";
|
|
6
|
+
import { defaultDrivers, remoteDriver } from "./drivers/registry.js";
|
|
7
|
+
import { AcpSettingsStore } from "./acp-settings.js";
|
|
7
8
|
import { SessionHub } from "./hub.js";
|
|
8
9
|
import { connectHost } from "./rpc/client.js";
|
|
9
10
|
import { HostRpcServer } from "./rpc/server.js";
|
|
@@ -82,11 +83,13 @@ export async function startHost(options) {
|
|
|
82
83
|
const machineId = loadMachineId(paths.home);
|
|
83
84
|
// Agents and terminals start clean even when LinkShell ran inside a Claude Code session.
|
|
84
85
|
const env = withoutClaudeSession(options.env ?? process.env);
|
|
86
|
+
const acpSettings = new AcpSettingsStore(join(paths.home, "acp.json"));
|
|
85
87
|
if (!options.drivers)
|
|
86
88
|
removeLegacyCopilotHooks(join(homedir(), ".copilot", "hooks"), log);
|
|
87
89
|
const drivers = options.drivers
|
|
88
90
|
? options.drivers(paths)
|
|
89
91
|
: defaultDrivers({
|
|
92
|
+
agents: acpSettings.agents(),
|
|
90
93
|
env,
|
|
91
94
|
hostVersion: options.version,
|
|
92
95
|
codexSocket: paths.codexSocket,
|
|
@@ -95,7 +98,23 @@ export async function startHost(options) {
|
|
|
95
98
|
claudeAdapter: options.claudeAdapter,
|
|
96
99
|
});
|
|
97
100
|
const store = new HostStore(paths.database);
|
|
98
|
-
const
|
|
101
|
+
const terminals = new TerminalManager(env, store);
|
|
102
|
+
const hub = new SessionHub(store, drivers, log, paths.home, {
|
|
103
|
+
settings: acpSettings,
|
|
104
|
+
createDriver: (agent) => remoteDriver(agent, { env, hostVersion: options.version }),
|
|
105
|
+
loginTerminal: async (_agent, launch) => {
|
|
106
|
+
const terminal = terminals.create({ launch, cols: 100, rows: 30 });
|
|
107
|
+
const exited = new Promise((resolve) => {
|
|
108
|
+
const stop = terminals.onChange((info, closed) => {
|
|
109
|
+
if (info.id === terminal.id && (closed || info.exitCode !== undefined)) {
|
|
110
|
+
stop();
|
|
111
|
+
resolve(info.exitCode ?? null);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
return { terminalId: terminal.id, exited };
|
|
116
|
+
},
|
|
117
|
+
});
|
|
99
118
|
await hub.start({ discoveryIntervalMs: options.discoveryIntervalMs });
|
|
100
119
|
const iceServers = options.iceServers ?? iceServersFromEnv(process.env.LINKSHELL_ICE_SERVERS);
|
|
101
120
|
const machineInfo = () => ({
|
|
@@ -109,7 +128,6 @@ export async function startHost(options) {
|
|
|
109
128
|
// LINKSHELL_ICE_SERVERS: STUN servers for the direct channel, comma separated ("off": never direct).
|
|
110
129
|
...(iceServers ? { direct: { iceServers } } : {}),
|
|
111
130
|
});
|
|
112
|
-
const terminals = new TerminalManager(env, store);
|
|
113
131
|
const server = new HostRpcServer({
|
|
114
132
|
hub,
|
|
115
133
|
terminals,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../../../host/src/host.ts"],"names":[],"mappings":"AAAA,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;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"host.js","sourceRoot":"","sources":["../../../../host/src/host.ts"],"names":[],"mappings":"AAAA,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;AAC5F,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,4DAA4D;AAC5D,MAAM,eAAe,GAAG,GAAG,CAAC;AAU5B,wEAAwE;AACxE,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAI,GAAG,WAAW,EAAE;IAC5C,IAAI,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC/B,IAAI,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QACxD,2FAA2F;QAC3F,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO;QACL,IAAI;QACJ,MAAM;QACN,UAAU,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;QACrC,WAAW,EAAE,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;QACvC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;KACjC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAA4B,CAAC;QACjF,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS;YAAE,OAAO,MAAM,CAAC,SAAS,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,oDAAoD;IACtD,CAAC;IACD,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;IAC/B,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzH,OAAO,SAAS,CAAC;AACnB,CAAC;AA6CD,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAAkB;IACpD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IAClD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,mBAAmB,CAAC;IACpD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACvD,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAoB;IAClD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,OAAe,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC,CAAC;IACvF,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1D,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/B,IAAI,MAAM,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5C,yFAAyF;IACzF,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IACvE,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,wBAAwB,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1F,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;QAC7B,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;QACxB,CAAC,CAAC,cAAc,CAAC;YACb,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE;YAC5B,GAAG;YACH,WAAW,EAAE,OAAO,CAAC,OAAO;YAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,aAAa,EAAE,OAAO,CAAC,aAAa;SACrC,CAAC,CAAC;IACP,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE;QAC1D,QAAQ,EAAE,WAAW;QACrB,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QACnF,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YACnE,MAAM,MAAM,GAAG,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;gBACpD,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;oBAC/C,IAAI,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,EAAE,CAAC;wBAAC,IAAI,EAAE,CAAC;wBAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;oBAAC,CAAC;gBACrH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC;QAC7C,CAAC;KACF,CAAC,CAAC;IACH,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAEtE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC9F,MAAM,WAAW,GAAG,GAAgB,EAAE,CAAC,CAAC;QACtC,SAAS;QACT,0FAA0F;QAC1F,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,QAAQ,EAAE;QAC1D,QAAQ,EAAE,QAAQ,EAAE;QACpB,WAAW,EAAE,OAAO,CAAC,OAAO;QAC5B,IAAI,EAAE,OAAO,EAAE;QACf,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;QACpB,qGAAqG;QACrG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClD,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC;QAC/B,GAAG;QACH,SAAS;QACT,WAAW;QACX,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG;KACJ,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAErB,IAAI,OAAgC,CAAC;IACrC,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAC/B,mGAAmG;IACnG,MAAM,WAAW,GAAG,GAAkB,EAAE;QACtC,MAAM,GAAG,GAAG,OAAO,MAAM,EAAE,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC;QAC3E,IAAI,OAAO,IAAI,OAAO,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACnC,OAAO,CAAC,cAAc,EAAE,CAAC;YACzB,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;QAC1B,CAAC;QACD,MAAM,QAAQ,GAAG,OAAO,CAAC;QACzB,OAAO,GAAG,GAAG;YACX,CAAC,CAAC,IAAI,WAAW,CAAC;gBACd,GAAG;gBACH,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,KAAK,EAAE,MAAM,EAAE,KAAK;gBACpB,IAAI,EAAE,MAAM,EAAE,IAAI;gBAClB,KAAK,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;gBAC/C,oDAAoD;gBACpD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;oBACnB,IAAI,OAAO,EAAE,GAAG,KAAK,GAAG;wBAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;gBACxE,CAAC;gBACD,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,CAAC;gBAClE,GAAG;aACJ,CAAC;YACJ,CAAC,CAAC,SAAS,CAAC;QACd,QAAQ,EAAE,IAAI,EAAE,CAAC;QACjB,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,EAAE,KAAK,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAc,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QAC5E,IAAI,QAAQ;YAAE,MAAM,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;QAC1D,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IACF,MAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACrC,WAAW,EAAE,CAAC;IAEd,IAAI,QAAmC,CAAC;IACxC,OAAO;QACL,KAAK;QACL,GAAG;QACH,MAAM;QACN,IAAI,OAAO;YACT,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,WAAW;QACX,IAAI;YACF,QAAQ,KAAK,CAAC,KAAK,IAAI,EAAE;gBACvB,OAAO,EAAE,IAAI,EAAE,CAAC;gBAChB,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBACpB,SAAS,CAAC,IAAI,EAAE,CAAC;gBACjB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBACjB,KAAK,CAAC,KAAK,EAAE,CAAC;YAChB,CAAC,CAAC,EAAE,CAAC;YACL,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/host/src/hub.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { type GoalChange, type BackgroundTask, type AgentInfo, type ContentBlock, type SessionEvent, type SessionSummary, type SubagentInfo, type QuestionAnswer, type GitInfo, type ProjectSummary, type WorktreeEntry } 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 } from "@linkshell/wire";
|
|
2
|
+
import type { AcpSettingsStore } from "./acp-settings.js";
|
|
2
3
|
import type { AgentDriver, DesktopController, DesktopLaunchContext, DriverHost, LaunchSpec } from "./drivers/types.js";
|
|
3
4
|
import { type HostStore } from "./store.js";
|
|
4
5
|
import { ComputerPreviews } from "./computer-preview.js";
|
|
5
6
|
export interface Subscriber {
|
|
6
7
|
event(event: SessionEvent): void;
|
|
8
|
+
notice?(notice: SessionNotice): void;
|
|
7
9
|
/** The backlog that follows starts after `startSeq`, not where the subscriber left off. */
|
|
8
10
|
window?(startSeq: number): void;
|
|
9
11
|
}
|
|
@@ -17,10 +19,13 @@ export declare class SessionHub {
|
|
|
17
19
|
private readonly log;
|
|
18
20
|
/** Where the host keeps its own files: worktrees go under it. */
|
|
19
21
|
private readonly home;
|
|
22
|
+
private readonly acpServices?;
|
|
20
23
|
private readonly drivers;
|
|
21
24
|
private readonly live;
|
|
22
25
|
private readonly summaryListeners;
|
|
23
26
|
private readonly removedListeners;
|
|
27
|
+
private readonly interactionListeners;
|
|
28
|
+
private readonly agentListeners;
|
|
24
29
|
private readonly auth;
|
|
25
30
|
/** Terminals (`linkshell <agent>`) currently driving handoff sessions. */
|
|
26
31
|
private readonly desktops;
|
|
@@ -32,7 +37,14 @@ export declare class SessionHub {
|
|
|
32
37
|
readonly driverHost: DriverHost;
|
|
33
38
|
constructor(store: HostStore, drivers: AgentDriver[], log?: (message: string) => void,
|
|
34
39
|
/** Where the host keeps its own files: worktrees go under it. */
|
|
35
|
-
home?: string
|
|
40
|
+
home?: string, acpServices?: {
|
|
41
|
+
settings: AcpSettingsStore;
|
|
42
|
+
createDriver(agent: AcpRemoteAgent): AgentDriver;
|
|
43
|
+
loginTerminal(agent: string, launch: LaunchSpec): Promise<{
|
|
44
|
+
terminalId: string;
|
|
45
|
+
exited: Promise<number | null>;
|
|
46
|
+
}>;
|
|
47
|
+
} | undefined);
|
|
36
48
|
start(options?: {
|
|
37
49
|
discoveryIntervalMs?: number;
|
|
38
50
|
}): Promise<void>;
|
|
@@ -41,6 +53,240 @@ export declare class SessionHub {
|
|
|
41
53
|
private refreshArchives;
|
|
42
54
|
stop(): Promise<void>;
|
|
43
55
|
agents(): AgentInfo[];
|
|
56
|
+
onInteraction(listener: (agent: string, request: PendingPermissionSummary | {
|
|
57
|
+
requestId: string;
|
|
58
|
+
resolved: true;
|
|
59
|
+
}) => void): () => void;
|
|
60
|
+
onAgent(listener: (agent: AgentInfo) => void): () => void;
|
|
61
|
+
acpInfo(agent: string, sessionId?: string): Promise<{
|
|
62
|
+
features: import("@linkshell/wire").AcpFeatures;
|
|
63
|
+
settings: AcpAgentSettings;
|
|
64
|
+
interactions: PendingPermissionSummary[];
|
|
65
|
+
}>;
|
|
66
|
+
pendingInteractions(): {
|
|
67
|
+
agent: string;
|
|
68
|
+
request: {
|
|
69
|
+
options: {
|
|
70
|
+
name: string;
|
|
71
|
+
kind: "allow_once" | "allow_always" | "reject_once" | "reject_always";
|
|
72
|
+
optionId: string;
|
|
73
|
+
}[];
|
|
74
|
+
title: string;
|
|
75
|
+
requestId: string;
|
|
76
|
+
toolCallId?: string | undefined;
|
|
77
|
+
tool?: {
|
|
78
|
+
title?: string | undefined;
|
|
79
|
+
kind?: "search" | "delete" | "other" | "read" | "edit" | "move" | "execute" | "think" | "fetch" | "switch_mode" | undefined;
|
|
80
|
+
content?: ({
|
|
81
|
+
type: "content";
|
|
82
|
+
content: {
|
|
83
|
+
type: "text";
|
|
84
|
+
text: string;
|
|
85
|
+
annotations?: import("zod").objectOutputType<{
|
|
86
|
+
audience: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodArray<import("zod").ZodEnum<["user", "assistant"]>, "many">>>, ("user" | "assistant")[] | undefined, ("user" | "assistant")[] | null | undefined>;
|
|
87
|
+
priority: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodNumber>>, number | undefined, number | null | undefined>;
|
|
88
|
+
lastModified: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, string | undefined, string | null | undefined>;
|
|
89
|
+
}, import("zod").ZodTypeAny, "passthrough"> | undefined;
|
|
90
|
+
_meta?: Record<string, unknown> | undefined;
|
|
91
|
+
} | {
|
|
92
|
+
type: "image";
|
|
93
|
+
mimeType: string;
|
|
94
|
+
annotations?: import("zod").objectOutputType<{
|
|
95
|
+
audience: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodArray<import("zod").ZodEnum<["user", "assistant"]>, "many">>>, ("user" | "assistant")[] | undefined, ("user" | "assistant")[] | null | undefined>;
|
|
96
|
+
priority: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodNumber>>, number | undefined, number | null | undefined>;
|
|
97
|
+
lastModified: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, string | undefined, string | null | undefined>;
|
|
98
|
+
}, import("zod").ZodTypeAny, "passthrough"> | undefined;
|
|
99
|
+
_meta?: Record<string, unknown> | undefined;
|
|
100
|
+
data?: string | undefined;
|
|
101
|
+
uri?: string | undefined;
|
|
102
|
+
} | {
|
|
103
|
+
type: "audio";
|
|
104
|
+
mimeType: string;
|
|
105
|
+
annotations?: import("zod").objectOutputType<{
|
|
106
|
+
audience: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodArray<import("zod").ZodEnum<["user", "assistant"]>, "many">>>, ("user" | "assistant")[] | undefined, ("user" | "assistant")[] | null | undefined>;
|
|
107
|
+
priority: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodNumber>>, number | undefined, number | null | undefined>;
|
|
108
|
+
lastModified: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, string | undefined, string | null | undefined>;
|
|
109
|
+
}, import("zod").ZodTypeAny, "passthrough"> | undefined;
|
|
110
|
+
_meta?: Record<string, unknown> | undefined;
|
|
111
|
+
data?: string | undefined;
|
|
112
|
+
uri?: string | undefined;
|
|
113
|
+
} | {
|
|
114
|
+
type: "resource";
|
|
115
|
+
resource: {
|
|
116
|
+
uri: string;
|
|
117
|
+
text?: string | undefined;
|
|
118
|
+
_meta?: Record<string, unknown> | undefined;
|
|
119
|
+
mimeType?: string | undefined;
|
|
120
|
+
blob?: string | undefined;
|
|
121
|
+
assetUri?: string | undefined;
|
|
122
|
+
};
|
|
123
|
+
annotations?: import("zod").objectOutputType<{
|
|
124
|
+
audience: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodArray<import("zod").ZodEnum<["user", "assistant"]>, "many">>>, ("user" | "assistant")[] | undefined, ("user" | "assistant")[] | null | undefined>;
|
|
125
|
+
priority: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodNumber>>, number | undefined, number | null | undefined>;
|
|
126
|
+
lastModified: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, string | undefined, string | null | undefined>;
|
|
127
|
+
}, import("zod").ZodTypeAny, "passthrough"> | undefined;
|
|
128
|
+
_meta?: Record<string, unknown> | undefined;
|
|
129
|
+
} | {
|
|
130
|
+
type: "resource_link";
|
|
131
|
+
uri: string;
|
|
132
|
+
name: string;
|
|
133
|
+
annotations?: import("zod").objectOutputType<{
|
|
134
|
+
audience: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodArray<import("zod").ZodEnum<["user", "assistant"]>, "many">>>, ("user" | "assistant")[] | undefined, ("user" | "assistant")[] | null | undefined>;
|
|
135
|
+
priority: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodNumber>>, number | undefined, number | null | undefined>;
|
|
136
|
+
lastModified: import("zod").ZodEffects<import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>, string | undefined, string | null | undefined>;
|
|
137
|
+
}, import("zod").ZodTypeAny, "passthrough"> | undefined;
|
|
138
|
+
_meta?: Record<string, unknown> | undefined;
|
|
139
|
+
mimeType?: string | undefined;
|
|
140
|
+
title?: string | undefined;
|
|
141
|
+
description?: string | undefined;
|
|
142
|
+
size?: number | undefined;
|
|
143
|
+
kind?: "file" | "skill" | "agent" | undefined;
|
|
144
|
+
};
|
|
145
|
+
} | {
|
|
146
|
+
path: string;
|
|
147
|
+
type: "diff";
|
|
148
|
+
newText: string;
|
|
149
|
+
oldText?: string | null | undefined;
|
|
150
|
+
} | {
|
|
151
|
+
path: string;
|
|
152
|
+
type: "patch";
|
|
153
|
+
diff: string;
|
|
154
|
+
change: "add" | "delete" | "update";
|
|
155
|
+
movePath?: string | undefined;
|
|
156
|
+
} | {
|
|
157
|
+
type: "terminal";
|
|
158
|
+
terminalId: string;
|
|
159
|
+
})[] | undefined;
|
|
160
|
+
rawInput?: unknown;
|
|
161
|
+
locations?: {
|
|
162
|
+
path: string;
|
|
163
|
+
line?: number | undefined;
|
|
164
|
+
}[] | undefined;
|
|
165
|
+
} | undefined;
|
|
166
|
+
detail?: string | undefined;
|
|
167
|
+
questions?: {
|
|
168
|
+
text: string;
|
|
169
|
+
kind: "text" | "choice" | "choices";
|
|
170
|
+
id: string;
|
|
171
|
+
options?: {
|
|
172
|
+
value: string;
|
|
173
|
+
label: string;
|
|
174
|
+
description?: string | undefined;
|
|
175
|
+
}[] | undefined;
|
|
176
|
+
other?: boolean | undefined;
|
|
177
|
+
constraints?: {
|
|
178
|
+
type: "string" | "number" | "boolean" | "integer" | "array";
|
|
179
|
+
minimum?: number | undefined;
|
|
180
|
+
maximum?: number | undefined;
|
|
181
|
+
minLength?: number | undefined;
|
|
182
|
+
maxLength?: number | undefined;
|
|
183
|
+
minItems?: number | undefined;
|
|
184
|
+
maxItems?: number | undefined;
|
|
185
|
+
pattern?: string | undefined;
|
|
186
|
+
format?: string | undefined;
|
|
187
|
+
} | undefined;
|
|
188
|
+
defaults?: string[] | undefined;
|
|
189
|
+
header?: string | undefined;
|
|
190
|
+
secret?: boolean | undefined;
|
|
191
|
+
required?: boolean | undefined;
|
|
192
|
+
}[] | undefined;
|
|
193
|
+
childSessionId?: string | undefined;
|
|
194
|
+
url?: {
|
|
195
|
+
url: string;
|
|
196
|
+
elicitationId: string;
|
|
197
|
+
} | undefined;
|
|
198
|
+
};
|
|
199
|
+
}[];
|
|
200
|
+
editor(input: EditorRequest): Promise<{
|
|
201
|
+
path?: string | undefined;
|
|
202
|
+
text?: string | undefined;
|
|
203
|
+
version?: number | undefined;
|
|
204
|
+
preview?: {
|
|
205
|
+
path: string;
|
|
206
|
+
kind: "edit" | "jump" | "searchAndReplace";
|
|
207
|
+
id: string;
|
|
208
|
+
before: string;
|
|
209
|
+
after: string;
|
|
210
|
+
position?: {
|
|
211
|
+
line: number;
|
|
212
|
+
character: number;
|
|
213
|
+
} | undefined;
|
|
214
|
+
} | undefined;
|
|
215
|
+
position?: {
|
|
216
|
+
line: number;
|
|
217
|
+
character: number;
|
|
218
|
+
} | undefined;
|
|
219
|
+
editorId?: string | undefined;
|
|
220
|
+
suggestions?: ({
|
|
221
|
+
uri: string;
|
|
222
|
+
kind: "edit";
|
|
223
|
+
id: string;
|
|
224
|
+
edits: {
|
|
225
|
+
newText: string;
|
|
226
|
+
range: {
|
|
227
|
+
start: {
|
|
228
|
+
line: number;
|
|
229
|
+
character: number;
|
|
230
|
+
};
|
|
231
|
+
end: {
|
|
232
|
+
line: number;
|
|
233
|
+
character: number;
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
}[];
|
|
237
|
+
cursorPosition?: {
|
|
238
|
+
line: number;
|
|
239
|
+
character: number;
|
|
240
|
+
} | null | undefined;
|
|
241
|
+
} | {
|
|
242
|
+
uri: string;
|
|
243
|
+
kind: "jump";
|
|
244
|
+
id: string;
|
|
245
|
+
position: {
|
|
246
|
+
line: number;
|
|
247
|
+
character: number;
|
|
248
|
+
};
|
|
249
|
+
} | {
|
|
250
|
+
uri: string;
|
|
251
|
+
kind: "rename";
|
|
252
|
+
id: string;
|
|
253
|
+
position: {
|
|
254
|
+
line: number;
|
|
255
|
+
character: number;
|
|
256
|
+
};
|
|
257
|
+
newName: string;
|
|
258
|
+
} | {
|
|
259
|
+
replace: string;
|
|
260
|
+
search: string;
|
|
261
|
+
uri: string;
|
|
262
|
+
kind: "searchAndReplace";
|
|
263
|
+
id: string;
|
|
264
|
+
isRegex?: boolean | null | undefined;
|
|
265
|
+
})[] | undefined;
|
|
266
|
+
}>;
|
|
267
|
+
configureAcp(agent: string, settings: AcpAgentSettings, sessionId?: string): Promise<void>;
|
|
268
|
+
authenticate(agent: string, methodId?: string, logout?: boolean): Promise<{
|
|
269
|
+
terminalId?: string;
|
|
270
|
+
}>;
|
|
271
|
+
respondInteraction(agent: string, requestId: string, response: {
|
|
272
|
+
optionId?: string;
|
|
273
|
+
answers?: QuestionAnswer[];
|
|
274
|
+
}): Promise<void>;
|
|
275
|
+
providers(agent: string, operation: "list" | "set" | "disable", config?: {
|
|
276
|
+
providerId: string;
|
|
277
|
+
apiType?: string;
|
|
278
|
+
baseUrl?: string;
|
|
279
|
+
headers?: Record<string, string>;
|
|
280
|
+
}): Promise<{
|
|
281
|
+
required: boolean;
|
|
282
|
+
providerId: string;
|
|
283
|
+
supported: string[];
|
|
284
|
+
current?: {
|
|
285
|
+
apiType: string;
|
|
286
|
+
baseUrl: string;
|
|
287
|
+
} | null | undefined;
|
|
288
|
+
}[]>;
|
|
289
|
+
customAgents(save?: AcpRemoteAgent, remove?: string): Promise<AcpRemoteAgent[]>;
|
|
44
290
|
/** Re-checks login state in the background when the cached answer is older than `maxAgeMs`. */
|
|
45
291
|
refreshAuthIfStale(maxAgeMs?: number): Promise<void>;
|
|
46
292
|
private refreshAuth;
|
|
@@ -127,6 +373,7 @@ export declare class SessionHub {
|
|
|
127
373
|
stopTask(sessionId: string, taskId: string): Promise<void>;
|
|
128
374
|
/** The sub-agents the session started, newest first. */
|
|
129
375
|
subagents(sessionId: string): SubagentInfo[];
|
|
376
|
+
cancelSubagent(sessionId: string, nativeSessionId: string): Promise<void>;
|
|
130
377
|
private subagentsOf;
|
|
131
378
|
/** Keeps the sub-agent tally current; true when it changed. */
|
|
132
379
|
private trackSubagent;
|
package/dist/host/src/hub.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RpcError, questionReplies, updateAsyncQuestions, sessionIdFor, } from "@linkshell/wire";
|
|
1
|
+
import { RpcError, questionReplies, questionAnswerError, updateAsyncQuestions, sessionIdFor, } from "@linkshell/wire";
|
|
2
2
|
import { inOrder } from "./drivers/acp/driver.js";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -91,10 +91,13 @@ export class SessionHub {
|
|
|
91
91
|
store;
|
|
92
92
|
log;
|
|
93
93
|
home;
|
|
94
|
+
acpServices;
|
|
94
95
|
drivers = new Map();
|
|
95
96
|
live = new Map();
|
|
96
97
|
summaryListeners = new Set();
|
|
97
98
|
removedListeners = new Set();
|
|
99
|
+
interactionListeners = new Set();
|
|
100
|
+
agentListeners = new Set();
|
|
98
101
|
auth = new Map();
|
|
99
102
|
/** Terminals (`linkshell <agent>`) currently driving handoff sessions. */
|
|
100
103
|
desktops = new Map();
|
|
@@ -104,6 +107,29 @@ export class SessionHub {
|
|
|
104
107
|
authRefreshing;
|
|
105
108
|
previews;
|
|
106
109
|
driverHost = {
|
|
110
|
+
authChanged: (agent) => {
|
|
111
|
+
const driver = this.drivers.get(agent);
|
|
112
|
+
if (!driver)
|
|
113
|
+
return;
|
|
114
|
+
void this.refreshAuth(driver).then(() => { const info = this.agents().find((entry) => entry.id === agent); if (info)
|
|
115
|
+
for (const listener of this.agentListeners)
|
|
116
|
+
listener(info); }).catch(() => { });
|
|
117
|
+
},
|
|
118
|
+
agentSettings: (agent) => this.acpServices.settings.settings(agent),
|
|
119
|
+
loginTerminal: async (agent, launch) => {
|
|
120
|
+
if (!this.acpServices)
|
|
121
|
+
throw RpcError.app("not_supported", "终端登录不可用");
|
|
122
|
+
return this.acpServices.loginTerminal(agent, launch);
|
|
123
|
+
},
|
|
124
|
+
interaction: (agent, request) => { for (const listener of this.interactionListeners)
|
|
125
|
+
listener(agent, request); },
|
|
126
|
+
notice: (agent, nativeId, notice) => {
|
|
127
|
+
const live = this.live.get(sessionIdFor(agent, nativeId));
|
|
128
|
+
if (!live || live.importing)
|
|
129
|
+
return;
|
|
130
|
+
for (const subscriber of live.subscribers)
|
|
131
|
+
subscriber.notice?.(notice);
|
|
132
|
+
},
|
|
107
133
|
tasks: (agent, nativeId) => {
|
|
108
134
|
const id = sessionIdFor(agent, nativeId);
|
|
109
135
|
return this.store.getSession(id) ? this.tasks(id) : [];
|
|
@@ -151,10 +177,13 @@ export class SessionHub {
|
|
|
151
177
|
};
|
|
152
178
|
constructor(store, drivers, log = () => { },
|
|
153
179
|
/** Where the host keeps its own files: worktrees go under it. */
|
|
154
|
-
home = join(homedir(), ".linkshell")) {
|
|
180
|
+
home = join(homedir(), ".linkshell"), acpServices) {
|
|
155
181
|
this.store = store;
|
|
156
182
|
this.log = log;
|
|
157
183
|
this.home = home;
|
|
184
|
+
this.acpServices = acpServices;
|
|
185
|
+
if (!acpServices)
|
|
186
|
+
this.driverHost.agentSettings = undefined;
|
|
158
187
|
const capture = process.platform === "darwin" ? new MacPreviewCapture(log) : undefined;
|
|
159
188
|
this.previews = new ComputerPreviews({
|
|
160
189
|
load: (id) => store.getDriverState(id, "computer-preview"),
|
|
@@ -241,6 +270,85 @@ export class SessionHub {
|
|
|
241
270
|
};
|
|
242
271
|
});
|
|
243
272
|
}
|
|
273
|
+
onInteraction(listener) {
|
|
274
|
+
this.interactionListeners.add(listener);
|
|
275
|
+
return () => { this.interactionListeners.delete(listener); };
|
|
276
|
+
}
|
|
277
|
+
onAgent(listener) { this.agentListeners.add(listener); return () => { this.agentListeners.delete(listener); }; }
|
|
278
|
+
async acpInfo(agent, sessionId) {
|
|
279
|
+
const driver = this.requireDriver(agent);
|
|
280
|
+
if (!driver.acpInfo)
|
|
281
|
+
throw RpcError.app("not_supported", "这个 AI 不使用 ACP");
|
|
282
|
+
const session = sessionId ? this.getSession(sessionId) : undefined;
|
|
283
|
+
if (session && session.agent !== agent)
|
|
284
|
+
throw RpcError.app("invalid_params", "会话与 AI 不匹配");
|
|
285
|
+
return driver.acpInfo(session?.nativeId);
|
|
286
|
+
}
|
|
287
|
+
pendingInteractions() { return [...this.drivers.values()].flatMap((driver) => (driver.pendingInteractions?.() ?? []).map((request) => ({ agent: driver.id, request }))); }
|
|
288
|
+
async editor(input) {
|
|
289
|
+
const driver = this.requireDriver(input.agent);
|
|
290
|
+
if (!driver.editor)
|
|
291
|
+
throw RpcError.app("not_supported", "这个 AI 不支持编辑建议");
|
|
292
|
+
return driver.editor(input);
|
|
293
|
+
}
|
|
294
|
+
async configureAcp(agent, settings, sessionId) {
|
|
295
|
+
const driver = this.requireDriver(agent);
|
|
296
|
+
if (!driver.configureAcp || !this.acpServices)
|
|
297
|
+
throw RpcError.app("not_supported", "这个 AI 不支持 ACP 配置");
|
|
298
|
+
const session = sessionId ? this.getSession(sessionId) : undefined;
|
|
299
|
+
if (session && session.agent !== agent)
|
|
300
|
+
throw RpcError.app("invalid_params", "会话与 AI 不匹配");
|
|
301
|
+
await driver.configureAcp(settings, session?.nativeId);
|
|
302
|
+
if (!session)
|
|
303
|
+
this.acpServices.settings.setSettings(agent, settings);
|
|
304
|
+
}
|
|
305
|
+
async authenticate(agent, methodId, logout) {
|
|
306
|
+
const driver = this.requireDriver(agent);
|
|
307
|
+
if (!driver.authenticate)
|
|
308
|
+
throw RpcError.app("not_supported", "这个 AI 不支持 ACP 登录");
|
|
309
|
+
const result = await driver.authenticate(methodId, logout);
|
|
310
|
+
await this.refreshAuth(driver);
|
|
311
|
+
return result;
|
|
312
|
+
}
|
|
313
|
+
async respondInteraction(agent, requestId, response) {
|
|
314
|
+
const driver = this.requireDriver(agent);
|
|
315
|
+
if (!driver.respondInteraction)
|
|
316
|
+
throw RpcError.app("not_supported", "这个 AI 不支持此交互");
|
|
317
|
+
await driver.respondInteraction(requestId, response);
|
|
318
|
+
}
|
|
319
|
+
async providers(agent, operation, config) {
|
|
320
|
+
const driver = this.requireDriver(agent);
|
|
321
|
+
if (!driver.providers)
|
|
322
|
+
throw RpcError.app("not_supported", "这个 AI 不支持供应商配置");
|
|
323
|
+
return driver.providers(operation, config);
|
|
324
|
+
}
|
|
325
|
+
async customAgents(save, remove) {
|
|
326
|
+
const services = this.acpServices;
|
|
327
|
+
if (!services)
|
|
328
|
+
throw RpcError.app("not_supported", "自定义 ACP 配置不可用");
|
|
329
|
+
if (save && remove)
|
|
330
|
+
throw RpcError.app("invalid_params", "不能同时保存和移除 Agent");
|
|
331
|
+
const id = save?.id ?? remove;
|
|
332
|
+
if (id) {
|
|
333
|
+
const existing = this.drivers.get(id);
|
|
334
|
+
if (existing && !services.settings.agents().some((agent) => agent.id === id))
|
|
335
|
+
throw RpcError.app("invalid_params", "不能覆盖或移除内置 AI");
|
|
336
|
+
const driver = save ? services.createDriver(save) : undefined;
|
|
337
|
+
if (existing) {
|
|
338
|
+
await existing.configureAcp?.(services.settings.settings(id));
|
|
339
|
+
await existing.stop();
|
|
340
|
+
this.drivers.delete(id);
|
|
341
|
+
}
|
|
342
|
+
if (save && driver) {
|
|
343
|
+
services.settings.saveAgent(save);
|
|
344
|
+
this.drivers.set(id, driver);
|
|
345
|
+
await driver.start(this.driverHost);
|
|
346
|
+
}
|
|
347
|
+
else
|
|
348
|
+
services.settings.removeAgent(id);
|
|
349
|
+
}
|
|
350
|
+
return services.settings.agents();
|
|
351
|
+
}
|
|
244
352
|
/** Re-checks login state in the background when the cached answer is older than `maxAgeMs`. */
|
|
245
353
|
refreshAuthIfStale(maxAgeMs = 60_000) {
|
|
246
354
|
if (this.authRefreshing)
|
|
@@ -352,9 +460,12 @@ export class SessionHub {
|
|
|
352
460
|
if (first) {
|
|
353
461
|
decorated.permission = {
|
|
354
462
|
requestId: first.requestId,
|
|
463
|
+
childSessionId: first.childSessionId,
|
|
355
464
|
toolCallId: first.toolCallId,
|
|
356
465
|
title: first.title,
|
|
357
466
|
detail: first.detail,
|
|
467
|
+
tool: first.tool,
|
|
468
|
+
url: first.url,
|
|
358
469
|
options: first.options,
|
|
359
470
|
questions: first.questions,
|
|
360
471
|
};
|
|
@@ -674,6 +785,8 @@ export class SessionHub {
|
|
|
674
785
|
task: detail?.task ?? call.title,
|
|
675
786
|
agentType: detail?.agentType,
|
|
676
787
|
name: detail?.name,
|
|
788
|
+
nativeSessionId: detail?.nativeSessionId,
|
|
789
|
+
canCancel: detail?.canCancel,
|
|
677
790
|
running,
|
|
678
791
|
failed: (workflow?.state ? workflow.state === "failed" : detail?.state ? detail.state === "failed" : state.status === "failed") || undefined,
|
|
679
792
|
startedAt: workflow?.startedAt ?? event.ts,
|
|
@@ -685,6 +798,13 @@ export class SessionHub {
|
|
|
685
798
|
}
|
|
686
799
|
return [...agents.values()].reverse();
|
|
687
800
|
}
|
|
801
|
+
async cancelSubagent(sessionId, nativeSessionId) {
|
|
802
|
+
const session = this.getSession(sessionId), driver = this.requireDriver(session.agent);
|
|
803
|
+
if (!driver.cancelSubagent)
|
|
804
|
+
throw RpcError.app("not_supported", "这个 AI 不支持单独停止子代理");
|
|
805
|
+
await this.ensureAttached(sessionId);
|
|
806
|
+
await driver.cancelSubagent(session.nativeId, nativeSessionId);
|
|
807
|
+
}
|
|
688
808
|
subagentsOf(sessionId, live) {
|
|
689
809
|
if (!live.subagents) {
|
|
690
810
|
live.subagents = new Map();
|
|
@@ -1032,9 +1152,11 @@ export class SessionHub {
|
|
|
1032
1152
|
const other = question.other ? answer.other?.trim() || undefined : undefined;
|
|
1033
1153
|
return [{ id: question.id, values, other }];
|
|
1034
1154
|
});
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1155
|
+
for (const question of request.questions) {
|
|
1156
|
+
const error = questionAnswerError(question, kept.find((answer) => answer.id === question.id));
|
|
1157
|
+
if (error)
|
|
1158
|
+
throw RpcError.app("invalid_params", error);
|
|
1159
|
+
}
|
|
1038
1160
|
await driver.answerQuestion(summary.nativeId, requestId, kept);
|
|
1039
1161
|
}
|
|
1040
1162
|
async desktopLaunch(agent, args, sessionId, context) {
|
|
@@ -1267,6 +1389,8 @@ export class SessionHub {
|
|
|
1267
1389
|
// A finished turn can't still be waiting on approvals; clear stale cards.
|
|
1268
1390
|
if (update.sessionUpdate === "ls_turn" && update.state === "ended" && !update.parentToolCallId) {
|
|
1269
1391
|
for (const requestId of [...live.permissions.keys()]) {
|
|
1392
|
+
if (live.permissions.get(requestId)?.childSessionId)
|
|
1393
|
+
continue;
|
|
1270
1394
|
this.commit(sessionId, { sessionUpdate: "ls_permission_resolved", requestId });
|
|
1271
1395
|
}
|
|
1272
1396
|
if (!live.importing)
|
|
@@ -1303,6 +1427,18 @@ export class SessionHub {
|
|
|
1303
1427
|
activityChanged = true;
|
|
1304
1428
|
};
|
|
1305
1429
|
switch (update.sessionUpdate) {
|
|
1430
|
+
case "ls_message": {
|
|
1431
|
+
if (update.parentToolCallId || !update.content)
|
|
1432
|
+
break;
|
|
1433
|
+
const text = update.content.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
1434
|
+
if (update.role === "user" && !before.title && text.trim())
|
|
1435
|
+
patch.title = compact(text, TITLE_LENGTH);
|
|
1436
|
+
if (update.role === "agent" && text.trim())
|
|
1437
|
+
patch.preview = compact(text, PREVIEW_LENGTH);
|
|
1438
|
+
if (live.turnActive && update.role === "agent")
|
|
1439
|
+
setActivity({ kind: "responding" });
|
|
1440
|
+
break;
|
|
1441
|
+
}
|
|
1306
1442
|
case "user_message_chunk": {
|
|
1307
1443
|
const text = update.content.type === "text" ? update.content.text : "";
|
|
1308
1444
|
if (text && !before.title)
|
|
@@ -1340,12 +1476,12 @@ export class SessionHub {
|
|
|
1340
1476
|
live.turnActive = update.state === "started";
|
|
1341
1477
|
setActivity(live.turnActive ? { kind: "thinking" } : undefined);
|
|
1342
1478
|
// Leftover permissions are cleared right after a turn ends (see commit).
|
|
1343
|
-
patch.state = live.turnActive ? "running" : update.stopReason === "error" ? "error" : "idle";
|
|
1479
|
+
patch.state = live.turnActive ? "running" : [...live.permissions.values()].some((permission) => permission.childSessionId) ? "waiting" : update.stopReason === "error" ? "error" : "idle";
|
|
1344
1480
|
break;
|
|
1345
1481
|
case "ls_status":
|
|
1346
1482
|
patch.state = update.state;
|
|
1347
1483
|
if (!live.importing) {
|
|
1348
|
-
live.turnActive = update.state === "running" || update.state === "waiting";
|
|
1484
|
+
live.turnActive = update.turnActive ?? (update.state === "running" || update.state === "waiting");
|
|
1349
1485
|
if (!live.turnActive)
|
|
1350
1486
|
setActivity(undefined);
|
|
1351
1487
|
}
|
|
@@ -1362,8 +1498,13 @@ export class SessionHub {
|
|
|
1362
1498
|
patch.state = live.turnActive ? "running" : "idle";
|
|
1363
1499
|
break;
|
|
1364
1500
|
case "session_info_update":
|
|
1365
|
-
if (update.title)
|
|
1366
|
-
patch.title = compact(update.title, TITLE_LENGTH);
|
|
1501
|
+
if (update.title !== undefined)
|
|
1502
|
+
patch.title = update.title === null ? null : compact(update.title, TITLE_LENGTH);
|
|
1503
|
+
if (update.updatedAt !== undefined) {
|
|
1504
|
+
const updatedAt = update.updatedAt === null ? before.createdAt : Date.parse(update.updatedAt);
|
|
1505
|
+
if (Number.isFinite(updatedAt))
|
|
1506
|
+
patch.updatedAt = updatedAt;
|
|
1507
|
+
}
|
|
1367
1508
|
if (update.model)
|
|
1368
1509
|
patch.model = update.model;
|
|
1369
1510
|
break;
|