@linkshell/host 0.1.0
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/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/host/src/drivers/acp/connection.d.ts +61 -0
- package/dist/host/src/drivers/acp/connection.js +121 -0
- package/dist/host/src/drivers/acp/connection.js.map +1 -0
- package/dist/host/src/drivers/acp/driver.d.ts +110 -0
- package/dist/host/src/drivers/acp/driver.js +438 -0
- package/dist/host/src/drivers/acp/driver.js.map +1 -0
- package/dist/host/src/drivers/acp/mapper.d.ts +49 -0
- package/dist/host/src/drivers/acp/mapper.js +411 -0
- package/dist/host/src/drivers/acp/mapper.js.map +1 -0
- package/dist/host/src/drivers/auth.d.ts +7 -0
- package/dist/host/src/drivers/auth.js +57 -0
- package/dist/host/src/drivers/auth.js.map +1 -0
- package/dist/host/src/drivers/claude/driver.d.ts +76 -0
- package/dist/host/src/drivers/claude/driver.js +331 -0
- package/dist/host/src/drivers/claude/driver.js.map +1 -0
- package/dist/host/src/drivers/claude/transcript.d.ts +335 -0
- package/dist/host/src/drivers/claude/transcript.js +516 -0
- package/dist/host/src/drivers/claude/transcript.js.map +1 -0
- package/dist/host/src/drivers/codex/app-server.d.ts +36 -0
- package/dist/host/src/drivers/codex/app-server.js +144 -0
- package/dist/host/src/drivers/codex/app-server.js.map +1 -0
- package/dist/host/src/drivers/codex/driver.d.ts +89 -0
- package/dist/host/src/drivers/codex/driver.js +452 -0
- package/dist/host/src/drivers/codex/driver.js.map +1 -0
- package/dist/host/src/drivers/codex/mapper.d.ts +61 -0
- package/dist/host/src/drivers/codex/mapper.js +623 -0
- package/dist/host/src/drivers/codex/mapper.js.map +1 -0
- package/dist/host/src/drivers/codex/settings.d.ts +51 -0
- package/dist/host/src/drivers/codex/settings.js +107 -0
- package/dist/host/src/drivers/codex/settings.js.map +1 -0
- package/dist/host/src/drivers/images.d.ts +11 -0
- package/dist/host/src/drivers/images.js +34 -0
- package/dist/host/src/drivers/images.js.map +1 -0
- package/dist/host/src/drivers/nesting.d.ts +6 -0
- package/dist/host/src/drivers/nesting.js +25 -0
- package/dist/host/src/drivers/nesting.js.map +1 -0
- package/dist/host/src/drivers/registry.d.ts +21 -0
- package/dist/host/src/drivers/registry.js +30 -0
- package/dist/host/src/drivers/registry.js.map +1 -0
- package/dist/host/src/drivers/types.d.ts +105 -0
- package/dist/host/src/drivers/types.js +2 -0
- package/dist/host/src/drivers/types.js.map +1 -0
- package/dist/host/src/fs.d.ts +69 -0
- package/dist/host/src/fs.js +198 -0
- package/dist/host/src/fs.js.map +1 -0
- package/dist/host/src/gateway.d.ts +55 -0
- package/dist/host/src/gateway.js +263 -0
- package/dist/host/src/gateway.js.map +1 -0
- package/dist/host/src/host.d.ts +51 -0
- package/dist/host/src/host.js +133 -0
- package/dist/host/src/host.js.map +1 -0
- package/dist/host/src/hub.d.ts +89 -0
- package/dist/host/src/hub.js +592 -0
- package/dist/host/src/hub.js.map +1 -0
- package/dist/host/src/index.d.ts +10 -0
- package/dist/host/src/index.js +10 -0
- package/dist/host/src/index.js.map +1 -0
- package/dist/host/src/ports.d.ts +24 -0
- package/dist/host/src/ports.js +201 -0
- package/dist/host/src/ports.js.map +1 -0
- package/dist/host/src/rpc/client.d.ts +9 -0
- package/dist/host/src/rpc/client.js +42 -0
- package/dist/host/src/rpc/client.js.map +1 -0
- package/dist/host/src/rpc/server.d.ts +52 -0
- package/dist/host/src/rpc/server.js +293 -0
- package/dist/host/src/rpc/server.js.map +1 -0
- package/dist/host/src/screen.d.ts +30 -0
- package/dist/host/src/screen.js +230 -0
- package/dist/host/src/screen.js.map +1 -0
- package/dist/host/src/shell-env.d.ts +8 -0
- package/dist/host/src/shell-env.js +39 -0
- package/dist/host/src/shell-env.js.map +1 -0
- package/dist/host/src/store.d.ts +75 -0
- package/dist/host/src/store.js +265 -0
- package/dist/host/src/store.js.map +1 -0
- package/dist/host/src/terminals.d.ts +55 -0
- package/dist/host/src/terminals.js +367 -0
- package/dist/host/src/terminals.js.map +1 -0
- package/dist/host/tsconfig.tsbuildinfo +1 -0
- package/dist/wire/src/crypto.d.ts +65 -0
- package/dist/wire/src/crypto.js +253 -0
- package/dist/wire/src/crypto.js.map +1 -0
- package/dist/wire/src/index.d.ts +8 -0
- package/dist/wire/src/index.js +9 -0
- package/dist/wire/src/index.js.map +1 -0
- package/dist/wire/src/model.d.ts +704 -0
- package/dist/wire/src/model.js +163 -0
- package/dist/wire/src/model.js.map +1 -0
- package/dist/wire/src/peer.d.ts +42 -0
- package/dist/wire/src/peer.js +143 -0
- package/dist/wire/src/peer.js.map +1 -0
- package/dist/wire/src/relay-client.d.ts +82 -0
- package/dist/wire/src/relay-client.js +259 -0
- package/dist/wire/src/relay-client.js.map +1 -0
- package/dist/wire/src/relay.d.ts +481 -0
- package/dist/wire/src/relay.js +129 -0
- package/dist/wire/src/relay.js.map +1 -0
- package/dist/wire/src/rpc.d.ts +4170 -0
- package/dist/wire/src/rpc.js +332 -0
- package/dist/wire/src/rpc.js.map +1 -0
- package/dist/wire/src/shell.d.ts +2 -0
- package/dist/wire/src/shell.js +62 -0
- package/dist/wire/src/shell.js.map +1 -0
- package/dist/wire/src/updates.d.ts +3325 -0
- package/dist/wire/src/updates.js +210 -0
- package/dist/wire/src/updates.js.map +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LinkShell Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @linkshell/host
|
|
2
|
+
|
|
3
|
+
The LinkShell v2 host daemon. It runs on your computer and owns what your devices control:
|
|
4
|
+
agent sessions (Codex, Claude Code, Gemini, Copilot, Grok and other ACP agents), terminals,
|
|
5
|
+
file and port previews. Devices reach it over the LAN or through a relay, end-to-end encrypted.
|
|
6
|
+
|
|
7
|
+
Run it through the CLI: `npm install -g linkshell-cli`, then `linkshell host --daemon`.
|
|
8
|
+
|
|
9
|
+
Requires Node.js 22.13 or newer.
|
|
10
|
+
|
|
11
|
+
MIT License
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type RpcId } from "@linkshell/wire";
|
|
2
|
+
/** The subset of ACP `initialize` results LinkShell reads. */
|
|
3
|
+
export interface AcpAgentCapabilities {
|
|
4
|
+
loadSession?: boolean;
|
|
5
|
+
promptCapabilities?: {
|
|
6
|
+
image?: boolean;
|
|
7
|
+
audio?: boolean;
|
|
8
|
+
embeddedContext?: boolean;
|
|
9
|
+
};
|
|
10
|
+
sessionCapabilities?: {
|
|
11
|
+
list?: unknown;
|
|
12
|
+
resume?: unknown;
|
|
13
|
+
close?: unknown;
|
|
14
|
+
fork?: unknown;
|
|
15
|
+
delete?: unknown;
|
|
16
|
+
};
|
|
17
|
+
_meta?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
export interface AcpInitializeResult {
|
|
20
|
+
protocolVersion?: number;
|
|
21
|
+
agentCapabilities?: AcpAgentCapabilities;
|
|
22
|
+
agentInfo?: {
|
|
23
|
+
name?: string;
|
|
24
|
+
version?: string;
|
|
25
|
+
};
|
|
26
|
+
authMethods?: {
|
|
27
|
+
id: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
}[];
|
|
30
|
+
_meta?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
export interface AcpConnectionOptions {
|
|
33
|
+
command: string;
|
|
34
|
+
args: string[];
|
|
35
|
+
env?: NodeJS.ProcessEnv;
|
|
36
|
+
cwd?: string;
|
|
37
|
+
clientVersion: string;
|
|
38
|
+
onUpdate: (sessionId: string, update: unknown) => void;
|
|
39
|
+
/** Agent → client requests (session/request_permission, …). */
|
|
40
|
+
onRequest: (method: string, params: unknown, id: RpcId) => unknown;
|
|
41
|
+
onExit: (reason: string) => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* One ACP agent subprocess: JSON-RPC 2.0 over newline-delimited stdio.
|
|
45
|
+
* A single process serves every session of that agent.
|
|
46
|
+
*/
|
|
47
|
+
export declare class AcpConnection {
|
|
48
|
+
private readonly options;
|
|
49
|
+
private child?;
|
|
50
|
+
private peer?;
|
|
51
|
+
private stopping;
|
|
52
|
+
private stderrTail;
|
|
53
|
+
initializeResult: AcpInitializeResult;
|
|
54
|
+
constructor(options: AcpConnectionOptions);
|
|
55
|
+
get capabilities(): AcpAgentCapabilities;
|
|
56
|
+
get alive(): boolean;
|
|
57
|
+
start(): Promise<AcpInitializeResult>;
|
|
58
|
+
request<T>(method: string, params: unknown, timeoutMs?: number): Promise<T>;
|
|
59
|
+
notify(method: string, params: unknown): void;
|
|
60
|
+
stop(): Promise<void>;
|
|
61
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { RpcPeer } from "@linkshell/wire";
|
|
3
|
+
/**
|
|
4
|
+
* One ACP agent subprocess: JSON-RPC 2.0 over newline-delimited stdio.
|
|
5
|
+
* A single process serves every session of that agent.
|
|
6
|
+
*/
|
|
7
|
+
export class AcpConnection {
|
|
8
|
+
options;
|
|
9
|
+
child;
|
|
10
|
+
peer;
|
|
11
|
+
stopping = false;
|
|
12
|
+
stderrTail = "";
|
|
13
|
+
initializeResult = {};
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
get capabilities() {
|
|
18
|
+
return this.initializeResult.agentCapabilities ?? {};
|
|
19
|
+
}
|
|
20
|
+
get alive() {
|
|
21
|
+
return Boolean(this.child && this.child.exitCode === null && !this.peer?.isClosed);
|
|
22
|
+
}
|
|
23
|
+
async start() {
|
|
24
|
+
this.stopping = false;
|
|
25
|
+
const child = spawn(this.options.command, this.options.args, {
|
|
26
|
+
env: this.options.env,
|
|
27
|
+
cwd: this.options.cwd,
|
|
28
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
29
|
+
});
|
|
30
|
+
this.child = child;
|
|
31
|
+
const peer = new RpcPeer({
|
|
32
|
+
send: (text) => {
|
|
33
|
+
if (child.stdin?.writable)
|
|
34
|
+
child.stdin.write(`${text}\n`);
|
|
35
|
+
},
|
|
36
|
+
onNotification: (method, params) => {
|
|
37
|
+
if (method !== "session/update")
|
|
38
|
+
return;
|
|
39
|
+
const payload = params;
|
|
40
|
+
if (typeof payload?.sessionId === "string")
|
|
41
|
+
this.options.onUpdate(payload.sessionId, payload.update);
|
|
42
|
+
},
|
|
43
|
+
onRequest: (method, params, id) => this.options.onRequest(method, params, id),
|
|
44
|
+
requestTimeoutMs: 0,
|
|
45
|
+
});
|
|
46
|
+
this.peer = peer;
|
|
47
|
+
// stdout chunks can split a JSON line; only hand complete lines to the peer.
|
|
48
|
+
let pending = "";
|
|
49
|
+
child.stdout?.on("data", (chunk) => {
|
|
50
|
+
pending += chunk.toString("utf8");
|
|
51
|
+
const end = pending.lastIndexOf("\n");
|
|
52
|
+
if (end < 0)
|
|
53
|
+
return;
|
|
54
|
+
const complete = pending.slice(0, end);
|
|
55
|
+
pending = pending.slice(end + 1);
|
|
56
|
+
peer.receive(complete);
|
|
57
|
+
});
|
|
58
|
+
child.stderr?.on("data", (chunk) => {
|
|
59
|
+
this.stderrTail = (this.stderrTail + chunk.toString("utf8")).slice(-4000);
|
|
60
|
+
});
|
|
61
|
+
const exited = new Promise((_, reject) => {
|
|
62
|
+
child.once("exit", (code, signal) => {
|
|
63
|
+
const reason = `${this.options.command} exited (${signal ?? code})${this.stderrTail ? `: ${this.stderrTail.trim().split("\n").slice(-3).join(" | ")}` : ""}`;
|
|
64
|
+
peer.close(reason);
|
|
65
|
+
if (!this.stopping)
|
|
66
|
+
this.options.onExit(reason);
|
|
67
|
+
reject(new Error(reason));
|
|
68
|
+
});
|
|
69
|
+
child.once("error", (error) => {
|
|
70
|
+
peer.close(error.message);
|
|
71
|
+
reject(error);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
exited.catch(() => { });
|
|
75
|
+
this.initializeResult = await Promise.race([
|
|
76
|
+
peer.request("initialize", {
|
|
77
|
+
protocolVersion: 1,
|
|
78
|
+
// LinkShell doesn't serve files or terminals to the agent: agents use their own tools.
|
|
79
|
+
clientCapabilities: {
|
|
80
|
+
fs: { readTextFile: false, writeTextFile: false },
|
|
81
|
+
terminal: false,
|
|
82
|
+
// Ask adapters that support it (Claude's) to stream sub-agents' own
|
|
83
|
+
// messages and tool calls, each stamped with the spawning call.
|
|
84
|
+
_meta: { "subagent-transcript": true },
|
|
85
|
+
},
|
|
86
|
+
clientInfo: { name: "linkshell", title: "LinkShell", version: this.options.clientVersion },
|
|
87
|
+
}, 30_000),
|
|
88
|
+
exited,
|
|
89
|
+
]);
|
|
90
|
+
return this.initializeResult;
|
|
91
|
+
}
|
|
92
|
+
request(method, params, timeoutMs = 60_000) {
|
|
93
|
+
if (!this.peer)
|
|
94
|
+
return Promise.reject(new Error("agent is not running"));
|
|
95
|
+
return this.peer.request(method, params, timeoutMs);
|
|
96
|
+
}
|
|
97
|
+
notify(method, params) {
|
|
98
|
+
this.peer?.notify(method, params);
|
|
99
|
+
}
|
|
100
|
+
async stop() {
|
|
101
|
+
this.stopping = true;
|
|
102
|
+
const child = this.child;
|
|
103
|
+
this.child = undefined;
|
|
104
|
+
this.peer?.close("stopping");
|
|
105
|
+
if (!child || child.exitCode !== null)
|
|
106
|
+
return;
|
|
107
|
+
child.stdin?.end();
|
|
108
|
+
child.kill("SIGTERM");
|
|
109
|
+
await new Promise((resolve) => {
|
|
110
|
+
const timer = setTimeout(() => {
|
|
111
|
+
child.kill("SIGKILL");
|
|
112
|
+
resolve();
|
|
113
|
+
}, 3000);
|
|
114
|
+
child.once("exit", () => {
|
|
115
|
+
clearTimeout(timer);
|
|
116
|
+
resolve();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../../../src/drivers/acp/connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { type AgentAuth, type AgentCapabilities, type AgentTier, type ContentBlock, type SessionUpdate } from "@linkshell/wire";
|
|
2
|
+
import type { AgentDriver, AttachContext, DiscoveredSession, DriverHost, DriverStatus, HistoryItem } from "../types.js";
|
|
3
|
+
import { AcpConnection } from "./connection.js";
|
|
4
|
+
import { AcpItemTracker, type SourcedConfigOption } from "./mapper.js";
|
|
5
|
+
export interface AcpAgentSpec {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
tier: AgentTier;
|
|
9
|
+
/** How to start the agent as an ACP server. */
|
|
10
|
+
command: string;
|
|
11
|
+
args: string[];
|
|
12
|
+
/** Detects the install; defaults to `<command> --version`. */
|
|
13
|
+
version?: {
|
|
14
|
+
command: string;
|
|
15
|
+
args: string[];
|
|
16
|
+
};
|
|
17
|
+
env?: Record<string, string>;
|
|
18
|
+
authStatus?: (env: NodeJS.ProcessEnv) => Promise<AgentAuth>;
|
|
19
|
+
/**
|
|
20
|
+
* Start the agent process with the host so its existing sessions show up
|
|
21
|
+
* (needs ACP session/list). Otherwise it starts on first use.
|
|
22
|
+
*/
|
|
23
|
+
discover?: boolean;
|
|
24
|
+
}
|
|
25
|
+
interface PendingPrompt {
|
|
26
|
+
content: ContentBlock[];
|
|
27
|
+
clientMessageId: string;
|
|
28
|
+
}
|
|
29
|
+
export interface AcpSessionState {
|
|
30
|
+
cwd: string;
|
|
31
|
+
loaded: boolean;
|
|
32
|
+
tracker: AcpItemTracker;
|
|
33
|
+
config: SourcedConfigOption[];
|
|
34
|
+
/** Set while a session/load replays history. */
|
|
35
|
+
replaying?: SessionUpdate[];
|
|
36
|
+
turnActive: boolean;
|
|
37
|
+
/** session/prompt requests in flight; with steering, a turn spans several. */
|
|
38
|
+
inflight: number;
|
|
39
|
+
queue: PendingPrompt[];
|
|
40
|
+
permissions: Map<string, (outcome: unknown) => void>;
|
|
41
|
+
}
|
|
42
|
+
/** Turns agent failures into something a person can act on. */
|
|
43
|
+
export declare function describeAgentError(label: string, message: string): {
|
|
44
|
+
message: string;
|
|
45
|
+
hint?: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* A generic Agent Client Protocol driver: one agent subprocess serving every
|
|
49
|
+
* session. Tier "remote" by default — LinkShell drives the agent, the desktop
|
|
50
|
+
* uses LinkShell's own UI. Subclasses add desktop handoff.
|
|
51
|
+
*/
|
|
52
|
+
export declare class AcpDriver implements AgentDriver {
|
|
53
|
+
protected readonly spec: AcpAgentSpec;
|
|
54
|
+
protected readonly options: {
|
|
55
|
+
env?: NodeJS.ProcessEnv;
|
|
56
|
+
hostVersion: string;
|
|
57
|
+
};
|
|
58
|
+
readonly id: string;
|
|
59
|
+
readonly label: string;
|
|
60
|
+
readonly tier: AgentTier;
|
|
61
|
+
capabilities: AgentCapabilities;
|
|
62
|
+
protected host?: DriverHost;
|
|
63
|
+
protected connection?: AcpConnection;
|
|
64
|
+
protected current: DriverStatus;
|
|
65
|
+
protected readonly sessions: Map<string, AcpSessionState>;
|
|
66
|
+
private restartTimer?;
|
|
67
|
+
private restartDelay;
|
|
68
|
+
private stopped;
|
|
69
|
+
private nextPermissionId;
|
|
70
|
+
constructor(spec: AcpAgentSpec, options: {
|
|
71
|
+
env?: NodeJS.ProcessEnv;
|
|
72
|
+
hostVersion: string;
|
|
73
|
+
});
|
|
74
|
+
status(): DriverStatus;
|
|
75
|
+
protected get env(): NodeJS.ProcessEnv;
|
|
76
|
+
authStatus(): Promise<AgentAuth>;
|
|
77
|
+
start(host: DriverHost): Promise<DriverStatus>;
|
|
78
|
+
stop(): Promise<void>;
|
|
79
|
+
listSessions(limit: number): Promise<DiscoveredSession[]>;
|
|
80
|
+
createSession(options: {
|
|
81
|
+
cwd: string;
|
|
82
|
+
model?: string;
|
|
83
|
+
}): Promise<DiscoveredSession>;
|
|
84
|
+
attach(nativeId: string, context: AttachContext): Promise<HistoryItem[]>;
|
|
85
|
+
detach(nativeId: string): Promise<void>;
|
|
86
|
+
prompt(nativeId: string, content: ContentBlock[], clientMessageId: string): Promise<"started" | "steered" | "queued">;
|
|
87
|
+
cancel(nativeId: string): Promise<void>;
|
|
88
|
+
respondPermission(nativeId: string, requestId: string, optionId: string): Promise<void>;
|
|
89
|
+
setConfig(nativeId: string, optionId: string, value: string): Promise<void>;
|
|
90
|
+
protected detectVersion(): Promise<string | undefined>;
|
|
91
|
+
private booting?;
|
|
92
|
+
/** Starts the agent process once; concurrent callers share the attempt. */
|
|
93
|
+
protected ensureStarted(): Promise<void>;
|
|
94
|
+
protected boot(): Promise<void>;
|
|
95
|
+
private scheduleRestart;
|
|
96
|
+
private onExit;
|
|
97
|
+
protected stateFor(nativeId: string, cwd: string): AcpSessionState;
|
|
98
|
+
protected emitConfig(nativeId: string, state: AcpSessionState): void;
|
|
99
|
+
/** Feeds an update through the item tracker and on to the host. */
|
|
100
|
+
protected emit(nativeId: string, update: SessionUpdate): void;
|
|
101
|
+
protected closeMessage(nativeId: string): void;
|
|
102
|
+
protected onUpdate(sessionId: string, raw: unknown): void;
|
|
103
|
+
private onRequest;
|
|
104
|
+
private cancelPermissions;
|
|
105
|
+
private echoUserMessage;
|
|
106
|
+
protected startTurn(nativeId: string, state: AcpSessionState, content: ContentBlock[], clientMessageId: string): void;
|
|
107
|
+
private sendPrompt;
|
|
108
|
+
protected rpc<T = unknown>(method: string, params: unknown, timeoutMs?: number): Promise<T>;
|
|
109
|
+
}
|
|
110
|
+
export {};
|