linkshell-cli 0.10.0 → 0.10.1

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.
@@ -1,17 +1,25 @@
1
1
  import { type RpcId } from "@linkshell/wire";
2
- export interface CodexAppServerOptions {
2
+ export interface CodexConnectionOptions {
3
3
  /** Unix socket path; must stay under the ~104-byte sun_path limit. */
4
4
  socketPath: string;
5
- command?: string;
6
- env?: NodeJS.ProcessEnv;
7
5
  clientVersion: string;
8
- log: (message: string) => void;
9
6
  onNotification: (method: string, params: unknown) => void;
10
7
  /** Server → client requests (approvals). Return ABANDON to leave one to other clients. */
11
8
  onRequest: (method: string, params: unknown, id: RpcId) => unknown;
12
9
  /** Called when the app-server process or its socket goes away unexpectedly. */
13
10
  onDown: (reason: string) => void;
14
11
  }
12
+ export interface CodexAppServerOptions extends CodexConnectionOptions {
13
+ command?: string;
14
+ env?: NodeJS.ProcessEnv;
15
+ log: (message: string) => void;
16
+ }
17
+ /**
18
+ * Where Codex's own background server listens: the one a plain `codex` in a
19
+ * terminal starts and talks to (`codex app-server daemon`), unless it is run
20
+ * with `--no-daemon` or with configuration overrides.
21
+ */
22
+ export declare function sharedServerSocket(env?: NodeJS.ProcessEnv): string;
15
23
  export declare function detectCodex(command?: string, env?: NodeJS.ProcessEnv): Promise<string | undefined>;
16
24
  /** The `--enable` arguments for the wanted features this Codex knows and hasn't removed. */
17
25
  export declare function featureArgs(command?: string, env?: NodeJS.ProcessEnv): Promise<string[]>;
@@ -21,8 +29,6 @@ export declare function enableArgs(listing: string): string[];
21
29
  * Owns one `codex app-server --listen unix://…` process and a client connection
22
30
  * to it. The same socket is what `codex --remote` attaches the desktop TUI to,
23
31
  * so the TUI and LinkShell are peers on the same threads.
24
- *
25
- * The socket speaks WebSocket; permessage-deflate must be off or the upgrade is reset.
26
32
  */
27
33
  export declare class CodexAppServer {
28
34
  private readonly options;
@@ -38,3 +44,22 @@ export declare class CodexAppServer {
38
44
  private connectWhenReady;
39
45
  private connect;
40
46
  }
47
+ /**
48
+ * A client connection to Codex's own background server, which this host
49
+ * neither starts nor stops. A thread that lives there (opened by a plain
50
+ * `codex`) can't be loaded anywhere else, but any number of clients can be on
51
+ * it there: joining it is how the phone and that terminal share a thread.
52
+ */
53
+ export declare class CodexSharedServer {
54
+ private readonly options;
55
+ private socket?;
56
+ private peer?;
57
+ private connecting?;
58
+ private closing;
59
+ constructor(options: CodexConnectionOptions);
60
+ /** Connects if that server is running; false when it isn't (it is never started from here). */
61
+ connect(): Promise<boolean>;
62
+ request<T>(method: string, params: unknown, timeoutMs?: number): Promise<T>;
63
+ close(): void;
64
+ private dropped;
65
+ }
@@ -1,5 +1,7 @@
1
1
  import { spawn, execFile } from "node:child_process";
2
2
  import { existsSync, rmSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
3
5
  import { promisify } from "node:util";
4
6
  import WebSocket from "ws";
5
7
  import { ABANDON, RpcPeer } from "@linkshell/wire";
@@ -20,6 +22,51 @@ const OPT_OUT_NOTIFICATIONS = [
20
22
  "thread/goal/cleared",
21
23
  "thread/goal/updated",
22
24
  ];
25
+ /**
26
+ * Where Codex's own background server listens: the one a plain `codex` in a
27
+ * terminal starts and talks to (`codex app-server daemon`), unless it is run
28
+ * with `--no-daemon` or with configuration overrides.
29
+ */
30
+ export function sharedServerSocket(env = process.env) {
31
+ return join(env.CODEX_HOME || join(homedir(), ".codex"), "app-server-control", "app-server-control.sock");
32
+ }
33
+ /** A client connection to an app-server socket. The socket speaks WebSocket; permessage-deflate must be off or the upgrade is reset. */
34
+ async function openConnection(options, closing) {
35
+ const socket = new WebSocket(`ws+unix://${options.socketPath}:/`, { perMessageDeflate: false, handshakeTimeout: 10_000 });
36
+ await new Promise((resolve, reject) => {
37
+ socket.once("open", () => resolve());
38
+ socket.once("error", reject);
39
+ });
40
+ const peer = new RpcPeer({
41
+ send: (text) => socket.send(text),
42
+ onNotification: (method, params) => options.onNotification(method, params),
43
+ onRequest: (method, params, id) => options.onRequest(method, params, id) ?? ABANDON,
44
+ requestTimeoutMs: 60_000,
45
+ });
46
+ socket.on("message", (data) => peer.receive(data.toString()));
47
+ let ready = false;
48
+ socket.on("close", () => {
49
+ peer.close("codex app-server connection closed");
50
+ if (ready && !closing())
51
+ options.onDown("codex app-server connection closed");
52
+ });
53
+ // A failure after the handshake arrives as "close" too; this only keeps it from being thrown.
54
+ socket.on("error", () => { });
55
+ try {
56
+ await peer.request("initialize", {
57
+ clientInfo: { name: "linkshell", title: "LinkShell", version: options.clientVersion },
58
+ // experimentalApi: plan mode (`collaborationMode`) and the questions Codex asks (`item/tool/requestUserInput`) need it.
59
+ capabilities: { experimentalApi: true, requestAttestation: false, optOutNotificationMethods: OPT_OUT_NOTIFICATIONS },
60
+ });
61
+ }
62
+ catch (error) {
63
+ socket.close();
64
+ throw error;
65
+ }
66
+ peer.notify("initialized", {});
67
+ ready = true;
68
+ return { socket, peer };
69
+ }
23
70
  export async function detectCodex(command = "codex", env) {
24
71
  try {
25
72
  const { stdout } = await execFileAsync(command, ["--version"], { env, timeout: 10_000 });
@@ -58,8 +105,6 @@ export function enableArgs(listing) {
58
105
  * Owns one `codex app-server --listen unix://…` process and a client connection
59
106
  * to it. The same socket is what `codex --remote` attaches the desktop TUI to,
60
107
  * so the TUI and LinkShell are peers on the same threads.
61
- *
62
- * The socket speaks WebSocket; permessage-deflate must be off or the upgrade is reset.
63
108
  */
64
109
  export class CodexAppServer {
65
110
  options;
@@ -140,31 +185,61 @@ export class CodexAppServer {
140
185
  throw new Error(`codex app-server did not accept connections: ${lastError instanceof Error ? lastError.message : "timeout"}`);
141
186
  }
142
187
  async connect() {
143
- const socket = new WebSocket(`ws+unix://${this.options.socketPath}:/`, { perMessageDeflate: false });
144
- await new Promise((resolve, reject) => {
145
- socket.once("open", () => resolve());
146
- socket.once("error", reject);
147
- });
148
- const peer = new RpcPeer({
149
- send: (text) => socket.send(text),
150
- onNotification: (method, params) => this.options.onNotification(method, params),
151
- onRequest: (method, params, id) => this.options.onRequest(method, params, id) ?? ABANDON,
152
- requestTimeoutMs: 60_000,
153
- });
154
- socket.on("message", (data) => peer.receive(data.toString()));
155
- socket.on("close", () => {
156
- peer.close("codex app-server connection closed");
157
- if (!this.stopping)
158
- this.options.onDown("codex app-server connection closed");
159
- });
188
+ const { socket, peer } = await openConnection(this.options, () => this.stopping);
160
189
  this.socket = socket;
161
190
  this.peer = peer;
162
- await peer.request("initialize", {
163
- clientInfo: { name: "linkshell", title: "LinkShell", version: this.options.clientVersion },
164
- // experimentalApi: plan mode (`collaborationMode`) and the questions Codex asks (`item/tool/requestUserInput`) need it.
165
- capabilities: { experimentalApi: true, requestAttestation: false, optOutNotificationMethods: OPT_OUT_NOTIFICATIONS },
191
+ }
192
+ }
193
+ /**
194
+ * A client connection to Codex's own background server, which this host
195
+ * neither starts nor stops. A thread that lives there (opened by a plain
196
+ * `codex`) can't be loaded anywhere else, but any number of clients can be on
197
+ * it there: joining it is how the phone and that terminal share a thread.
198
+ */
199
+ export class CodexSharedServer {
200
+ options;
201
+ socket;
202
+ peer;
203
+ connecting;
204
+ closing = false;
205
+ constructor(options) {
206
+ this.options = options;
207
+ }
208
+ /** Connects if that server is running; false when it isn't (it is never started from here). */
209
+ connect() {
210
+ if (this.peer)
211
+ return Promise.resolve(true);
212
+ if (!existsSync(this.options.socketPath))
213
+ return Promise.resolve(false);
214
+ this.closing = false;
215
+ this.connecting ??= openConnection({ ...this.options, onDown: (reason) => this.dropped(reason) }, () => this.closing)
216
+ .then(({ socket, peer }) => {
217
+ this.socket = socket;
218
+ this.peer = peer;
219
+ return true;
220
+ })
221
+ .catch(() => false)
222
+ .finally(() => {
223
+ this.connecting = undefined;
166
224
  });
167
- peer.notify("initialized", {});
225
+ return this.connecting;
226
+ }
227
+ request(method, params, timeoutMs) {
228
+ if (!this.peer)
229
+ return Promise.reject(new Error("Codex's background server is not connected"));
230
+ return this.peer.request(method, params, timeoutMs);
231
+ }
232
+ close() {
233
+ this.closing = true;
234
+ this.peer?.close("stopping");
235
+ this.socket?.close();
236
+ this.peer = undefined;
237
+ this.socket = undefined;
238
+ }
239
+ dropped(reason) {
240
+ this.peer = undefined;
241
+ this.socket = undefined;
242
+ this.options.onDown(reason);
168
243
  }
169
244
  }
170
245
  //# sourceMappingURL=app-server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-server.js","sourceRoot":"","sources":["../../../../../../host/src/drivers/codex/app-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app-server.js","sourceRoot":"","sources":["../../../../../../host/src/drivers/codex/app-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /** Where the desktop app's bus listens (the first Codex window to start serves it). */
2
+ export declare function desktopBusSocket(env?: NodeJS.ProcessEnv): string;
3
+ /**
4
+ * Asks whichever Codex window runs `threadId` to stop its turn, as its own
5
+ * Stop button does. Resolves when that window says it has; rejects when no
6
+ * window runs the thread, none answers in time, or the app isn't there.
7
+ */
8
+ export declare function interruptThroughDesktop(socketPath: string, threadId: string, turnId: string | undefined, timeoutMs?: number): Promise<void>;
@@ -0,0 +1,124 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { existsSync } from "node:fs";
3
+ import { connect } from "node:net";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ // The Codex desktop app and its IDE extension keep the threads they run in
7
+ // app-servers of their own, which take no other clients. What they do share
8
+ // is a message bus between their windows, on a socket in Codex's home: a
9
+ // window that doesn't run a thread asks the one that does to act on it
10
+ // ("thread-follower-…" requests). That is the only way to stop a turn the
11
+ // desktop app is running without stopping the app.
12
+ //
13
+ // The bus is the app's own, not a published interface: each method carries a
14
+ // version the receiver checks, and a request it doesn't recognise is refused.
15
+ // So everything here is one question with a yes or a no; a no is reported as
16
+ // "can't be stopped from here", never retried in another shape.
17
+ /** Where the desktop app's bus listens (the first Codex window to start serves it). */
18
+ export function desktopBusSocket(env = process.env) {
19
+ return join(env.CODEX_HOME || join(homedir(), ".codex"), "ipc", "ipc.sock");
20
+ }
21
+ /** The version of the interrupt request the installed apps accept: with the turn it is meant for, or (older) without. */
22
+ const INTERRUPT_VERSION = { withTurn: 4, anyTurn: 3 };
23
+ function frame(message) {
24
+ const body = Buffer.from(JSON.stringify(message), "utf8");
25
+ const head = Buffer.alloc(4);
26
+ head.writeUInt32LE(body.length, 0);
27
+ return Buffer.concat([head, body]);
28
+ }
29
+ /**
30
+ * Asks whichever Codex window runs `threadId` to stop its turn, as its own
31
+ * Stop button does. Resolves when that window says it has; rejects when no
32
+ * window runs the thread, none answers in time, or the app isn't there.
33
+ */
34
+ export async function interruptThroughDesktop(socketPath, threadId, turnId, timeoutMs = 12_000) {
35
+ if (!existsSync(socketPath))
36
+ throw new Error("the Codex desktop app isn't running");
37
+ const socket = connect(socketPath);
38
+ try {
39
+ await new Promise((resolve, reject) => {
40
+ socket.once("connect", resolve);
41
+ socket.once("error", reject);
42
+ });
43
+ const bus = busClient(socket, timeoutMs);
44
+ const registered = await bus.request("initialize", { clientType: "linkshell" }, 0);
45
+ bus.clientId = String(registered.clientId ?? "");
46
+ await bus.request("thread-follower-interrupt-turn", { conversationId: threadId, mode: "user-stop", ...(turnId ? { expectedTurnId: turnId } : {}) }, turnId ? INTERRUPT_VERSION.withTurn : INTERRUPT_VERSION.anyTurn);
47
+ }
48
+ finally {
49
+ socket.destroy();
50
+ }
51
+ }
52
+ /** One connection to the bus: length-prefixed JSON, requests answered by `requestId`. */
53
+ function busClient(socket, timeoutMs) {
54
+ const pending = new Map();
55
+ let buffered = Buffer.alloc(0);
56
+ const failAll = (error) => {
57
+ for (const waiting of pending.values())
58
+ waiting.reject(error);
59
+ pending.clear();
60
+ };
61
+ socket.on("data", (chunk) => {
62
+ buffered = Buffer.concat([buffered, chunk]);
63
+ while (buffered.length >= 4) {
64
+ const length = buffered.readUInt32LE(0);
65
+ if (buffered.length < 4 + length)
66
+ return;
67
+ let message;
68
+ try {
69
+ message = JSON.parse(buffered.subarray(4, 4 + length).toString("utf8"));
70
+ }
71
+ catch {
72
+ failAll(new Error("the Codex desktop app sent something unreadable"));
73
+ socket.destroy();
74
+ return;
75
+ }
76
+ buffered = buffered.subarray(4 + length);
77
+ if (message.type === "response") {
78
+ const waiting = pending.get(String(message.requestId));
79
+ if (!waiting)
80
+ continue;
81
+ pending.delete(String(message.requestId));
82
+ if (message.resultType === "success")
83
+ waiting.resolve(message.result);
84
+ else
85
+ waiting.reject(new Error(String(message.error ?? "refused")));
86
+ }
87
+ else if (message.type === "client-discovery-request") {
88
+ // The bus asks every client whether it runs the thread a request is about. Left
89
+ // unanswered, the asker waits ten seconds for this client; it never does.
90
+ socket.write(frame({ type: "client-discovery-response", requestId: message.requestId, response: { canHandle: false } }));
91
+ }
92
+ else if (message.type === "request") {
93
+ socket.write(frame({ type: "response", requestId: message.requestId, resultType: "error", error: "no-handler-for-request" }));
94
+ }
95
+ }
96
+ });
97
+ socket.on("close", () => failAll(new Error("the Codex desktop app closed the connection")));
98
+ socket.on("error", () => { });
99
+ const client = {
100
+ clientId: "initializing-client",
101
+ request(method, params, version) {
102
+ const requestId = randomUUID();
103
+ return new Promise((resolve, reject) => {
104
+ const timer = setTimeout(() => {
105
+ pending.delete(requestId);
106
+ reject(new Error("timeout"));
107
+ }, timeoutMs);
108
+ pending.set(requestId, {
109
+ resolve: (result) => {
110
+ clearTimeout(timer);
111
+ resolve(result);
112
+ },
113
+ reject: (error) => {
114
+ clearTimeout(timer);
115
+ reject(error);
116
+ },
117
+ });
118
+ socket.write(frame({ type: "request", requestId, sourceClientId: client.clientId, version, method, params, timeoutMs }));
119
+ });
120
+ },
121
+ };
122
+ return client;
123
+ }
124
+ //# sourceMappingURL=desktop-ipc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"desktop-ipc.js","sourceRoot":"","sources":["../../../../../../host/src/drivers/codex/desktop-ipc.ts"],"names":[],"mappings":"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"}
@@ -8,10 +8,31 @@ export interface CodexDriverOptions {
8
8
  hostVersion: string;
9
9
  /** Delay before restarting a crashed app-server; doubles up to 30s. */
10
10
  restartDelayMs?: number;
11
+ /**
12
+ * Codex's own background server, joined for the threads that live in it
13
+ * (default: where the installed Codex keeps it). `false`: never.
14
+ */
15
+ sharedSocketPath?: string | false;
16
+ /**
17
+ * The bus the Codex desktop app's windows share, through which it is asked
18
+ * to stop a turn it runs (default: where the installed Codex keeps it). `false`: never.
19
+ */
20
+ desktopBusPath?: string | false;
21
+ /** How often a thread that can only be read is looked at again. */
22
+ observeIntervalMs?: number;
23
+ /** How long a thread no device has open, and that isn't working, stays joined. */
24
+ releaseDelayMs?: number;
11
25
  }
12
26
  /**
13
27
  * Codex, tier multi_client: LinkShell and the desktop TUI (`codex --remote`)
14
28
  * are both clients of one app-server, so every thread is live on both sides.
29
+ *
30
+ * Codex lets one process have a thread loaded at a time. A thread loaded in
31
+ * Codex's own background server (a plain `codex` in a terminal) is joined
32
+ * there, as one more client. A thread in a process that takes no other
33
+ * clients (the desktop app, an IDE extension) is read from disk as that
34
+ * process writes it; a message for it goes into Codex's own queue, which that
35
+ * process starts when it is idle; and its turn is stopped by asking the app.
15
36
  */
16
37
  export declare class CodexDriver implements AgentDriver {
17
38
  private readonly options;
@@ -52,12 +73,35 @@ export declare class CodexDriver implements AgentDriver {
52
73
  private restartTimer?;
53
74
  private restartDelay;
54
75
  private stopped;
76
+ private shared?;
77
+ /** Threads loaded in Codex's background server: announced by it, or joined there. */
78
+ private readonly inShared;
79
+ private readonly observed;
80
+ private readonly takingUp;
81
+ /** Messages in Codex's own queue for threads another process holds: it starts them when it is idle or its turn ends. */
82
+ private readonly waiting;
83
+ /** Notifications for a thread being joined, held until its history has been read. */
84
+ private readonly joining;
85
+ /** Threads a device has open. */
86
+ private readonly watching;
87
+ /** Threads another client of this host's app-server opened (`linkshell codex`): followed for as long as the host runs. */
88
+ private readonly kept;
89
+ /** Threads started here that have had no turn: nothing on disk yet to come back to. */
90
+ private readonly fresh;
91
+ private readonly releases;
92
+ /** What was last announced per thread (settings, commands): a thread joined again says it only if it changed. */
93
+ private readonly announced;
94
+ private readonly observeIntervalMs;
95
+ private readonly releaseDelayMs;
96
+ private readonly sharedSocketPath?;
97
+ private readonly desktopBusPath?;
55
98
  constructor(options: CodexDriverOptions);
56
99
  status(): DriverStatus;
57
100
  start(host: DriverHost): Promise<DriverStatus>;
58
101
  authStatus(): Promise<AgentAuth>;
59
102
  stop(): Promise<void>;
60
103
  listSessions(limit: number): Promise<DiscoveredSession[]>;
104
+ private stateElsewhere;
61
105
  /** Codex's own fork: a new thread with this one's turns, all or through `lastTurnId`, in `cwd`. */
62
106
  fork(nativeId: string, options: ForkOptions): Promise<DiscoveredSession>;
63
107
  createSession(options: {
@@ -65,10 +109,67 @@ export declare class CodexDriver implements AgentDriver {
65
109
  model?: string;
66
110
  }): Promise<DiscoveredSession>;
67
111
  attach(nativeId: string): Promise<HistoryItem[]>;
112
+ /**
113
+ * Subscribes to a thread where it can be driven: this host's app-server, or
114
+ * Codex's background server when the thread is loaded there. What the
115
+ * server sends from then on is held until `goLive`.
116
+ */
117
+ private subscribe;
118
+ /** thread/resume where the thread can be loaded: here, or in Codex's background server if it is loaded there. */
119
+ private resume;
120
+ /** Says where the joined thread stands, then lets through what arrived while it was being read. */
121
+ private goLive;
122
+ /** Reads the thread from disk, and keeps reading while a device has it open. */
123
+ private observe;
124
+ /** Reports what the other process has finished since the last look. An item still being written isn't on disk. */
125
+ private refresh;
126
+ private report;
127
+ private stopObserving;
128
+ /**
129
+ * Before writing to a thread that could only be read: joins it if whoever
130
+ * had it has let go, with what they wrote since the last look. Still held,
131
+ * it stays as it is.
132
+ */
133
+ private takeUp;
134
+ /**
135
+ * Leaves a message in Codex's own queue for a thread another process holds.
136
+ * That process starts it when it is idle, or right after the turn it is running.
137
+ */
138
+ private enqueue;
139
+ /** What waits in Codex's queue for the thread, as the session's queue on the devices. */
140
+ private syncWaiting;
141
+ private setWaiting;
142
+ unqueue(nativeId: string, clientMessageId: string): boolean;
143
+ /** A queued message can't be put into the turn another Codex is running: that turn is stopped, and the queue goes on. */
144
+ sendQueuedNow(nativeId: string): Promise<void>;
145
+ /**
146
+ * Stops the turn a Codex that can't be joined is running. Only its own app
147
+ * can: it is asked over the bus its windows share.
148
+ */
149
+ private stopElsewhere;
150
+ /**
151
+ * What a message for a thread held elsewhere is refused with. The apps show
152
+ * a failed message without its reason, so the reason goes into the session too.
153
+ */
154
+ private refused;
155
+ /** Says, when it becomes so and when it is over, that the session is in a Codex this host can't join. */
156
+ private reportHeld;
157
+ /** Whether a device has the session open. */
158
+ watched(nativeId: string, open: boolean): void;
159
+ private cancelRelease;
160
+ /**
161
+ * A thread stays loaded, and closed to every other Codex program, for as
162
+ * long as this host is subscribed to it. One that no device has open and
163
+ * that isn't working is let go after a while, so the desktop app or a
164
+ * `codex` in a terminal can open it; opening it on a device joins it again.
165
+ */
166
+ private considerRelease;
167
+ private release;
168
+ private hasApprovals;
68
169
  /** Nests each sub-agent's own history right after the call that spawned it. */
69
170
  private withSubAgents;
70
171
  detach(nativeId: string): Promise<void>;
71
- prompt(nativeId: string, content: ContentBlock[], clientMessageId: string): Promise<"started" | "steered">;
172
+ prompt(nativeId: string, content: ContentBlock[], clientMessageId: string): Promise<"started" | "steered" | "queued">;
72
173
  /**
73
174
  * `/compact`, `/review`, `/init` and `/<skill>`: what the TUI does for them,
74
175
  * through the app-server. Anything else is an ordinary message.
@@ -79,6 +180,7 @@ export declare class CodexDriver implements AgentDriver {
79
180
  setConfig(nativeId: string, optionId: string, value: string): Promise<void>;
80
181
  private loadModels;
81
182
  private announceConfig;
183
+ private isNews;
82
184
  private loadSkills;
83
185
  /** What `/` offers on a device: the commands above, then the skills this project can use. */
84
186
  private announceCommands;
@@ -88,7 +190,7 @@ export declare class CodexDriver implements AgentDriver {
88
190
  cancel(nativeId: string): Promise<void>;
89
191
  respondPermission(nativeId: string, requestId: string, optionId: string): Promise<void>;
90
192
  answerQuestion(nativeId: string, requestId: string, answers: QuestionAnswer[]): Promise<void>;
91
- desktopLaunch(args: string[], nativeId?: string): LaunchSpec;
193
+ desktopLaunch(args: string[], nativeId?: string): Promise<LaunchSpec>;
92
194
  private boot;
93
195
  /** Follows every thread already live in the app-server (e.g. opened in the TUI). */
94
196
  private reattachLoaded;
@@ -107,4 +209,11 @@ export declare class CodexDriver implements AgentDriver {
107
209
  private onServerRequest;
108
210
  private rememberTitle;
109
211
  private rpc;
212
+ /** A request about a thread, to the server it is loaded in. */
213
+ private rpcFor;
214
+ /** Connects to Codex's background server, if it is running. */
215
+ private connectShared;
216
+ /** Codex's background server went away (stopped, or restarted by its updater): the threads joined there are joined again wherever they are now. */
217
+ private onSharedDown;
218
+ private abandonApprovals;
110
219
  }