linkshell-cli 0.9.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.
Files changed (168) hide show
  1. package/README.md +11 -6
  2. package/dist/cli/src/commands/gateway.d.ts +2 -0
  3. package/dist/cli/src/commands/gateway.js +29 -0
  4. package/dist/cli/src/commands/gateway.js.map +1 -0
  5. package/dist/cli/src/commands/host.d.ts +2 -0
  6. package/dist/cli/src/commands/host.js +12 -2
  7. package/dist/cli/src/commands/host.js.map +1 -1
  8. package/dist/cli/src/commands/setup.js +4 -2
  9. package/dist/cli/src/commands/setup.js.map +1 -1
  10. package/dist/cli/src/index.js +12 -334
  11. package/dist/cli/src/index.js.map +1 -1
  12. package/dist/cli/src/utils/daemon.d.ts +0 -7
  13. package/dist/cli/src/utils/daemon.js +1 -18
  14. package/dist/cli/src/utils/daemon.js.map +1 -1
  15. package/dist/cli/tsconfig.tsbuildinfo +1 -1
  16. package/dist/gateway/src/accounts.d.ts +6 -0
  17. package/dist/gateway/src/accounts.js +32 -0
  18. package/dist/gateway/src/accounts.js.map +1 -0
  19. package/dist/gateway/src/index.d.ts +4 -0
  20. package/dist/gateway/src/index.js +5 -0
  21. package/dist/gateway/src/index.js.map +1 -0
  22. package/dist/gateway/src/main.d.ts +1 -0
  23. package/dist/gateway/src/main.js +57 -0
  24. package/dist/gateway/src/main.js.map +1 -0
  25. package/dist/gateway/src/rate-limit.d.ts +22 -0
  26. package/dist/gateway/src/rate-limit.js +66 -0
  27. package/dist/gateway/src/rate-limit.js.map +1 -0
  28. package/dist/gateway/src/relay.d.ts +84 -0
  29. package/dist/gateway/src/relay.js +434 -0
  30. package/dist/gateway/src/relay.js.map +1 -0
  31. package/dist/gateway/src/serve.d.ts +34 -0
  32. package/dist/gateway/src/serve.js +102 -0
  33. package/dist/gateway/src/serve.js.map +1 -0
  34. package/dist/gateway/src/store.d.ts +35 -0
  35. package/dist/gateway/src/store.js +109 -0
  36. package/dist/gateway/src/store.js.map +1 -0
  37. package/dist/gateway/src/subscription.d.ts +18 -0
  38. package/dist/gateway/src/subscription.js +46 -0
  39. package/dist/gateway/src/subscription.js.map +1 -0
  40. package/dist/host/src/drivers/claude/transcript.d.ts +23 -23
  41. package/dist/host/src/drivers/codex/app-server.d.ts +31 -6
  42. package/dist/host/src/drivers/codex/app-server.js +99 -24
  43. package/dist/host/src/drivers/codex/app-server.js.map +1 -1
  44. package/dist/host/src/drivers/codex/desktop-ipc.d.ts +8 -0
  45. package/dist/host/src/drivers/codex/desktop-ipc.js +124 -0
  46. package/dist/host/src/drivers/codex/desktop-ipc.js.map +1 -0
  47. package/dist/host/src/drivers/codex/driver.d.ts +111 -2
  48. package/dist/host/src/drivers/codex/driver.js +629 -63
  49. package/dist/host/src/drivers/codex/driver.js.map +1 -1
  50. package/dist/host/src/drivers/codex/mapper.d.ts +21 -0
  51. package/dist/host/src/drivers/codex/mapper.js +45 -6
  52. package/dist/host/src/drivers/codex/mapper.js.map +1 -1
  53. package/dist/host/src/drivers/types.d.ts +6 -0
  54. package/dist/host/src/fs.d.ts +3 -3
  55. package/dist/host/src/hub.js +8 -1
  56. package/dist/host/src/hub.js.map +1 -1
  57. package/dist/host/src/input.d.ts +4 -4
  58. package/dist/host/src/terminals.d.ts +1 -1
  59. package/dist/wire/src/model.d.ts +6 -6
  60. package/dist/wire/src/relay.d.ts +8 -8
  61. package/dist/wire/src/rpc.d.ts +818 -818
  62. package/dist/wire/src/updates.d.ts +18 -18
  63. package/package.json +5 -14
  64. package/src/commands/gateway.ts +31 -0
  65. package/src/commands/host.ts +13 -2
  66. package/src/commands/setup.ts +4 -2
  67. package/src/index.ts +14 -429
  68. package/src/utils/daemon.ts +2 -27
  69. package/dist/cli/src/config.d.ts +0 -14
  70. package/dist/cli/src/config.js +0 -23
  71. package/dist/cli/src/config.js.map +0 -1
  72. package/dist/cli/src/machine-id.d.ts +0 -6
  73. package/dist/cli/src/machine-id.js +0 -50
  74. package/dist/cli/src/machine-id.js.map +0 -1
  75. package/dist/cli/src/providers.d.ts +0 -13
  76. package/dist/cli/src/providers.js +0 -77
  77. package/dist/cli/src/providers.js.map +0 -1
  78. package/dist/cli/src/runtime/acp/acp-client.d.ts +0 -128
  79. package/dist/cli/src/runtime/acp/acp-client.js +0 -430
  80. package/dist/cli/src/runtime/acp/acp-client.js.map +0 -1
  81. package/dist/cli/src/runtime/acp/agent-process-discovery.d.ts +0 -48
  82. package/dist/cli/src/runtime/acp/agent-process-discovery.js +0 -278
  83. package/dist/cli/src/runtime/acp/agent-process-discovery.js.map +0 -1
  84. package/dist/cli/src/runtime/acp/agent-session.d.ts +0 -62
  85. package/dist/cli/src/runtime/acp/agent-session.js +0 -1081
  86. package/dist/cli/src/runtime/acp/agent-session.js.map +0 -1
  87. package/dist/cli/src/runtime/acp/agent-workspace.d.ts +0 -311
  88. package/dist/cli/src/runtime/acp/agent-workspace.js +0 -6083
  89. package/dist/cli/src/runtime/acp/agent-workspace.js.map +0 -1
  90. package/dist/cli/src/runtime/acp/claude-sdk-client.d.ts +0 -121
  91. package/dist/cli/src/runtime/acp/claude-sdk-client.js +0 -1147
  92. package/dist/cli/src/runtime/acp/claude-sdk-client.js.map +0 -1
  93. package/dist/cli/src/runtime/acp/claude-stream-json-client.d.ts +0 -53
  94. package/dist/cli/src/runtime/acp/claude-stream-json-client.js +0 -552
  95. package/dist/cli/src/runtime/acp/claude-stream-json-client.js.map +0 -1
  96. package/dist/cli/src/runtime/acp/json-rpc.d.ts +0 -22
  97. package/dist/cli/src/runtime/acp/json-rpc.js +0 -160
  98. package/dist/cli/src/runtime/acp/json-rpc.js.map +0 -1
  99. package/dist/cli/src/runtime/acp/provider-resolver.d.ts +0 -16
  100. package/dist/cli/src/runtime/acp/provider-resolver.js +0 -132
  101. package/dist/cli/src/runtime/acp/provider-resolver.js.map +0 -1
  102. package/dist/cli/src/runtime/bridge-session.d.ts +0 -97
  103. package/dist/cli/src/runtime/bridge-session.js +0 -1364
  104. package/dist/cli/src/runtime/bridge-session.js.map +0 -1
  105. package/dist/cli/src/runtime/hook-command.d.ts +0 -9
  106. package/dist/cli/src/runtime/hook-command.js +0 -80
  107. package/dist/cli/src/runtime/hook-command.js.map +0 -1
  108. package/dist/cli/src/runtime/screen-fallback.d.ts +0 -22
  109. package/dist/cli/src/runtime/screen-fallback.js +0 -202
  110. package/dist/cli/src/runtime/screen-fallback.js.map +0 -1
  111. package/dist/cli/src/runtime/screen-share.d.ts +0 -34
  112. package/dist/cli/src/runtime/screen-share.js +0 -376
  113. package/dist/cli/src/runtime/screen-share.js.map +0 -1
  114. package/dist/cli/src/runtime/scrollback.d.ts +0 -11
  115. package/dist/cli/src/runtime/scrollback.js +0 -33
  116. package/dist/cli/src/runtime/scrollback.js.map +0 -1
  117. package/dist/cli/src/runtime/usage-report.d.ts +0 -34
  118. package/dist/cli/src/runtime/usage-report.js +0 -332
  119. package/dist/cli/src/runtime/usage-report.js.map +0 -1
  120. package/dist/cli/src/utils/keep-awake.d.ts +0 -6
  121. package/dist/cli/src/utils/keep-awake.js +0 -48
  122. package/dist/cli/src/utils/keep-awake.js.map +0 -1
  123. package/dist/cli/src/utils/lan-ip.d.ts +0 -9
  124. package/dist/cli/src/utils/lan-ip.js +0 -61
  125. package/dist/cli/src/utils/lan-ip.js.map +0 -1
  126. package/dist/host/src/input-helper.d.ts +0 -1
  127. package/dist/host/src/input-helper.js +0 -564
  128. package/dist/host/src/input-helper.js.map +0 -1
  129. package/dist/shared-protocol/src/index.d.ts +0 -17029
  130. package/dist/shared-protocol/src/index.js +0 -1064
  131. package/dist/shared-protocol/src/index.js.map +0 -1
  132. package/scripts/copy-web.mjs +0 -34
  133. package/src/config.ts +0 -36
  134. package/src/machine-id.ts +0 -61
  135. package/src/providers.ts +0 -97
  136. package/src/runtime/acp/acp-client.ts +0 -521
  137. package/src/runtime/acp/agent-process-discovery.ts +0 -308
  138. package/src/runtime/acp/agent-session.ts +0 -1186
  139. package/src/runtime/acp/agent-workspace.ts +0 -6590
  140. package/src/runtime/acp/claude-sdk-client.ts +0 -1221
  141. package/src/runtime/acp/claude-stream-json-client.ts +0 -633
  142. package/src/runtime/acp/json-rpc.ts +0 -184
  143. package/src/runtime/acp/provider-resolver.ts +0 -154
  144. package/src/runtime/bridge-session.ts +0 -1549
  145. package/src/runtime/hook-command.ts +0 -73
  146. package/src/runtime/screen-fallback.ts +0 -235
  147. package/src/runtime/screen-share.ts +0 -426
  148. package/src/runtime/scrollback.ts +0 -43
  149. package/src/runtime/usage-report.ts +0 -342
  150. package/src/utils/keep-awake.ts +0 -61
  151. package/src/utils/lan-ip.ts +0 -61
  152. package/web/assets/FileBrowser-wOAfY-xj.js +0 -2
  153. package/web/assets/PortPreview-Cbvv7yzA.js +0 -2
  154. package/web/assets/TerminalPanel-CTSklkct.js +0 -1
  155. package/web/assets/UsageDashboard-CJ1AsNmd.js +0 -1
  156. package/web/assets/index-BsnDaeCt.css +0 -33
  157. package/web/assets/index-q85psYZ5.js +0 -36
  158. package/web/assets/vendor-highlight-NXWAAyIv.js +0 -5
  159. package/web/assets/vendor-react-M7xG3qiw.js +0 -49
  160. package/web/assets/vendor-xterm-D15THcI4.js +0 -9
  161. package/web/index.html +0 -37
  162. package/web/logos/claude.png +0 -0
  163. package/web/logos/codex.png +0 -0
  164. package/web/logos/copilot.png +0 -0
  165. package/web/logos/gemini.png +0 -0
  166. package/web/logos/linkshell-1024.png +0 -0
  167. package/web/logos/linkshell-splash.png +0 -0
  168. package/web/logos/linkshell.png +0 -0
@@ -1,521 +0,0 @@
1
- import { JsonRpcStdioTransport } from "./json-rpc.js";
2
- import type { AgentFraming, AgentProtocol } from "./provider-resolver.js";
3
-
4
- type AgentPermissionMode = "read_only" | "workspace_write" | "full_access";
5
- type AgentCollaborationMode = "default" | "plan";
6
-
7
- export interface AdvertisedAcpCapabilities {
8
- protocolVersion?: number;
9
- loadSession: boolean;
10
- listSession: boolean;
11
- forkSession: boolean;
12
- setModel: boolean;
13
- cancel: boolean;
14
- images: boolean;
15
- audio: boolean;
16
- embeddedContext: boolean;
17
- mcp: boolean;
18
- methods: string[];
19
- }
20
-
21
- function asRecord(value: unknown): Record<string, unknown> | undefined {
22
- return value && typeof value === "object" && !Array.isArray(value)
23
- ? value as Record<string, unknown>
24
- : undefined;
25
- }
26
-
27
- function truthy(value: unknown): boolean {
28
- return value === true || value === "true" || value === 1;
29
- }
30
-
31
- function collectAdvertisedMethods(...sources: Array<Record<string, unknown> | undefined>): string[] {
32
- const methods = new Set<string>();
33
- for (const source of sources) {
34
- if (!source) continue;
35
- const lists = [source.methods, source.supportedMethods, source.sessionMethods];
36
- for (const list of lists) {
37
- if (!Array.isArray(list)) continue;
38
- for (const item of list) {
39
- if (typeof item === "string" && item.trim()) methods.add(item.trim());
40
- }
41
- }
42
- for (const [key, value] of Object.entries(source)) {
43
- if (key.includes("/") && truthy(value)) methods.add(key);
44
- }
45
- }
46
- return [...methods];
47
- }
48
-
49
- /** Parse ACP `initialize` result. Unadvertised optional methods stay false. */
50
- export function parseAcpInitializeCapabilities(result: unknown): AdvertisedAcpCapabilities {
51
- const raw = asRecord(result) ?? {};
52
- const agentCaps = asRecord(raw.agentCapabilities) ?? asRecord(raw.capabilities) ?? {};
53
- const sessionCaps = asRecord(raw.sessionCapabilities) ?? asRecord(agentCaps.sessionCapabilities) ?? {};
54
- const promptCaps = asRecord(agentCaps.promptCapabilities) ?? {};
55
- const mcpCaps = asRecord(agentCaps.mcpCapabilities);
56
- const methods = collectAdvertisedMethods(raw, agentCaps, sessionCaps);
57
- const has = (...keys: string[]) =>
58
- keys.some((key) => truthy(agentCaps[key]) || truthy(sessionCaps[key]) || methods.includes(key));
59
- return {
60
- protocolVersion: typeof raw.protocolVersion === "number" ? raw.protocolVersion : undefined,
61
- loadSession: has("loadSession", "session/load"),
62
- listSession: has("listSession", "listSessions", "session/list"),
63
- forkSession: has("forkSession", "session/fork"),
64
- setModel: has("setModel", "session/set_model"),
65
- // ACP v1 MUST session/cancel. AgentCapabilities has no cancel flag, so a
66
- // real-shaped initialize that omits it still supports cancel.
67
- cancel: true,
68
- images: truthy(promptCaps.image) || has("image", "images"),
69
- audio: truthy(promptCaps.audio) || has("audio"),
70
- embeddedContext: truthy(promptCaps.embeddedContext),
71
- mcp: Boolean(mcpCaps && Object.keys(mcpCaps).length > 0) || has("mcp"),
72
- methods,
73
- };
74
- }
75
-
76
- function normalizeMcpServers(value: unknown): unknown[] {
77
- if (Array.isArray(value)) return value;
78
- if (!value || typeof value !== "object") return [];
79
- return Object.entries(value as Record<string, unknown>).map(([name, config]) => {
80
- if (config && typeof config === "object" && !Array.isArray(config)) {
81
- return { name, ...config as Record<string, unknown> };
82
- }
83
- return { name, config };
84
- });
85
- }
86
-
87
- // Codex prefers a named permission profile (`permissions: ":workspace"`) and
88
- // rejects combining it with the legacy tagged `sandboxPolicy`. Map our three
89
- // LinkShell modes onto official profile ids; keep sandboxPolicy only for
90
- // full_access, which has no stable named profile in the public docs.
91
- function permissionOverridesForMode(
92
- mode: AgentPermissionMode | undefined,
93
- ): { permissions?: string; sandboxPolicy?: { type: string } } {
94
- if (!mode) return {};
95
- switch (mode) {
96
- case "read_only":
97
- return { permissions: ":read-only" };
98
- case "workspace_write":
99
- return { permissions: ":workspace" };
100
- case "full_access":
101
- return { sandboxPolicy: { type: "dangerFullAccess" } };
102
- default:
103
- return {};
104
- }
105
- }
106
-
107
- function approvalPolicyForMode(
108
- mode: AgentPermissionMode | undefined,
109
- ): string | undefined {
110
- if (!mode) return undefined;
111
- switch (mode) {
112
- case "read_only":
113
- case "workspace_write":
114
- // Ask before escaping the sandbox → triggers a permission request the
115
- // host forwards to the client as agent.v2.permission.request.
116
- return "on-request";
117
- case "full_access":
118
- return "never";
119
- default:
120
- return undefined;
121
- }
122
- }
123
-
124
- export class AcpClient {
125
- private readonly transport: JsonRpcStdioTransport;
126
- private readonly protocol: AgentProtocol;
127
- lastInitializeResult: unknown;
128
- advertised: AdvertisedAcpCapabilities = parseAcpInitializeCapabilities(undefined);
129
-
130
- constructor(input: {
131
- command: string;
132
- protocol: AgentProtocol;
133
- framing: AgentFraming;
134
- cwd: string;
135
- onNotification: (method: string, params: unknown) => void;
136
- onRequest: (method: string, params: unknown) => Promise<unknown> | unknown;
137
- onExit: (message: string) => void;
138
- }) {
139
- this.protocol = input.protocol;
140
- this.transport = new JsonRpcStdioTransport(
141
- input.command,
142
- input.framing,
143
- input.onNotification,
144
- input.onRequest,
145
- input.onExit,
146
- );
147
- this.transport.start(input.cwd);
148
- }
149
-
150
- async initialize(): Promise<unknown> {
151
- if (this.protocol === "codex-app-server") {
152
- const result = await this.transport.request("initialize", {
153
- clientInfo: { name: "LinkShell", version: "0.1" },
154
- capabilities: { experimentalApi: true },
155
- });
156
- this.transport.notify("initialized", {});
157
- this.lastInitializeResult = result;
158
- this.advertised = {
159
- ...parseAcpInitializeCapabilities(result),
160
- loadSession: true,
161
- listSession: true,
162
- forkSession: true,
163
- setModel: true,
164
- cancel: true,
165
- images: true,
166
- mcp: true,
167
- };
168
- return result;
169
- }
170
- const result = await this.transport.request("initialize", {
171
- protocolVersion: 1,
172
- clientInfo: { name: "LinkShell", version: "0.1" },
173
- clientCapabilities: {
174
- fs: { readTextFile: false, writeTextFile: false },
175
- terminal: false,
176
- },
177
- });
178
- this.lastInitializeResult = result;
179
- this.advertised = { ...parseAcpInitializeCapabilities(result), cancel: true };
180
- return result;
181
- }
182
-
183
- newSession(input: { cwd: string; mcpServers?: unknown }): Promise<unknown> {
184
- if (this.protocol === "codex-app-server") {
185
- return this.transport.request("thread/start", {
186
- cwd: input.cwd,
187
- sessionStartSource: "startup",
188
- });
189
- }
190
- return this.transport.request("session/new", {
191
- cwd: input.cwd,
192
- mcpServers: normalizeMcpServers(input.mcpServers),
193
- });
194
- }
195
-
196
- loadSession(input: { sessionId: string; cwd: string; mcpServers?: unknown }): Promise<unknown> {
197
- if (this.protocol === "codex-app-server") {
198
- return this.transport.request("thread/resume", {
199
- threadId: input.sessionId,
200
- cwd: input.cwd,
201
- excludeTurns: false,
202
- });
203
- }
204
- if (!this.advertised.loadSession) {
205
- return Promise.reject(new Error("Provider did not advertise session/load."));
206
- }
207
- return this.transport.request("session/load", {
208
- sessionId: input.sessionId,
209
- cwd: input.cwd,
210
- mcpServers: normalizeMcpServers(input.mcpServers),
211
- });
212
- }
213
-
214
- readSession(input: { sessionId: string; includeTurns?: boolean }): Promise<unknown> {
215
- if (this.protocol === "codex-app-server") {
216
- return this.transport.request("thread/read", {
217
- threadId: input.sessionId,
218
- includeTurns: input.includeTurns ?? true,
219
- });
220
- }
221
- return Promise.reject(new Error("Provider does not support readSession."));
222
- }
223
-
224
- listTurns(input: {
225
- sessionId: string;
226
- limit?: number;
227
- cursor?: string;
228
- sortDirection?: "asc" | "desc";
229
- itemsView?: "summary" | "full";
230
- }): Promise<unknown> {
231
- if (this.protocol === "codex-app-server") {
232
- return this.transport.request("thread/turns/list", {
233
- threadId: input.sessionId,
234
- limit: input.limit ?? 50,
235
- cursor: input.cursor,
236
- sortDirection: input.sortDirection ?? "desc",
237
- itemsView: input.itemsView ?? "full",
238
- });
239
- }
240
- return Promise.reject(new Error("Provider does not support listTurns."));
241
- }
242
-
243
- async listSessions(): Promise<unknown> {
244
- if (this.protocol !== "codex-app-server") {
245
- if (!this.advertised.listSession) {
246
- return Promise.reject(new Error("Provider did not advertise session/list."));
247
- }
248
- return this.transport.request("session/list", {});
249
- }
250
- const allThreads: unknown[] = [];
251
- let cursor: string | undefined;
252
- for (let page = 0; page < 10; page++) {
253
- const result = await this.transport.request("thread/list", {
254
- limit: 100,
255
- ...(cursor ? { cursor } : {}),
256
- });
257
- const raw = result && typeof result === "object" ? result as Record<string, unknown> : undefined;
258
- const threads =
259
- Array.isArray(result) ? result :
260
- Array.isArray(raw?.data) ? raw.data :
261
- Array.isArray(raw?.threads) ? raw.threads :
262
- Array.isArray(raw?.sessions) ? raw.sessions :
263
- Array.isArray(raw?.items) ? raw.items :
264
- [];
265
- allThreads.push(...threads);
266
- const nextCursor =
267
- (typeof raw?.nextCursor === "string" && raw.nextCursor) ||
268
- (typeof raw?.next_cursor === "string" && raw.next_cursor) ||
269
- undefined;
270
- if (!nextCursor || threads.length === 0) break;
271
- cursor = nextCursor;
272
- }
273
- return { data: allThreads };
274
- }
275
-
276
- updateThreadSettings(input: {
277
- sessionId: string;
278
- model?: string;
279
- reasoningEffort?: string;
280
- permissionMode?: AgentPermissionMode;
281
- collaborationMode?: AgentCollaborationMode;
282
- cwd?: string;
283
- }): Promise<unknown> {
284
- if (this.protocol !== "codex-app-server") {
285
- return Promise.reject(new Error("Provider does not support updateThreadSettings."));
286
- }
287
- const model = input.model?.trim();
288
- const collaborationMode = input.collaborationMode && input.collaborationMode !== "default"
289
- ? {
290
- mode: input.collaborationMode,
291
- settings: {
292
- model: model || "default",
293
- ...(input.reasoningEffort ? { reasoning_effort: input.reasoningEffort } : {}),
294
- },
295
- }
296
- : undefined;
297
- const permission = permissionOverridesForMode(input.permissionMode);
298
- const approvalPolicy = approvalPolicyForMode(input.permissionMode);
299
- return this.transport.request("thread/settings/update", {
300
- threadId: input.sessionId,
301
- ...(model ? { model } : {}),
302
- ...(input.reasoningEffort ? { effort: input.reasoningEffort } : {}),
303
- ...permission,
304
- ...(approvalPolicy ? { approvalPolicy } : {}),
305
- ...(collaborationMode ? { collaborationMode } : {}),
306
- });
307
- }
308
-
309
- setThreadName(input: { sessionId: string; name: string }): Promise<unknown> {
310
- if (this.protocol !== "codex-app-server") {
311
- return Promise.reject(new Error("Provider does not support setThreadName."));
312
- }
313
- return this.transport.request("thread/name/set", {
314
- threadId: input.sessionId,
315
- name: input.name,
316
- });
317
- }
318
-
319
- archiveThread(input: { sessionId: string }): Promise<unknown> {
320
- if (this.protocol !== "codex-app-server") {
321
- return Promise.reject(new Error("Provider does not support archiveThread."));
322
- }
323
- return this.transport.request("thread/archive", { threadId: input.sessionId });
324
- }
325
-
326
- unarchiveThread(input: { sessionId: string }): Promise<unknown> {
327
- if (this.protocol !== "codex-app-server") {
328
- return Promise.reject(new Error("Provider does not support unarchiveThread."));
329
- }
330
- return this.transport.request("thread/unarchive", { threadId: input.sessionId });
331
- }
332
-
333
- deleteThread(input: { sessionId: string }): Promise<unknown> {
334
- if (this.protocol !== "codex-app-server") {
335
- return Promise.reject(new Error("Provider does not support deleteThread."));
336
- }
337
- return this.transport.request("thread/delete", { threadId: input.sessionId });
338
- }
339
-
340
- forkThread(input: { sessionId: string; lastTurnId?: string }): Promise<unknown> {
341
- if (this.protocol === "codex-app-server") {
342
- return this.transport.request("thread/fork", {
343
- threadId: input.sessionId,
344
- ...(input.lastTurnId ? { lastTurnId: input.lastTurnId } : {}),
345
- });
346
- }
347
- if (!this.advertised.forkSession) {
348
- return Promise.reject(new Error("Provider did not advertise session/fork."));
349
- }
350
- return this.transport.request("session/fork", {
351
- sessionId: input.sessionId,
352
- ...(input.lastTurnId ? { lastTurnId: input.lastTurnId } : {}),
353
- });
354
- }
355
-
356
- setSessionModel(input: { sessionId: string; modelId: string }): Promise<unknown> {
357
- if (this.protocol === "codex-app-server") {
358
- return this.updateThreadSettings({ sessionId: input.sessionId, model: input.modelId });
359
- }
360
- if (!this.advertised.setModel) {
361
- return Promise.reject(new Error("Provider did not advertise session/set_model."));
362
- }
363
- return this.transport.request("session/set_model", {
364
- sessionId: input.sessionId,
365
- modelId: input.modelId,
366
- });
367
- }
368
-
369
- startReview(input: { sessionId: string; prompt?: string }): Promise<unknown> {
370
- if (this.protocol !== "codex-app-server") {
371
- return Promise.reject(new Error("Provider does not support startReview."));
372
- }
373
- const prompt = input.prompt?.trim();
374
- return this.transport.request("review/start", {
375
- threadId: input.sessionId,
376
- target: prompt
377
- ? { type: "custom", instructions: prompt }
378
- : { type: "uncommittedChanges" },
379
- });
380
- }
381
-
382
- listSkills(input: { cwd: string }): Promise<unknown> {
383
- if (this.protocol !== "codex-app-server") {
384
- return Promise.reject(new Error("Provider does not support listSkills."));
385
- }
386
- return this.transport.request("skills/list", { cwds: [input.cwd] });
387
- }
388
-
389
- listMcpServers(input?: { threadId?: string }): Promise<unknown> {
390
- if (this.protocol !== "codex-app-server") {
391
- return Promise.reject(new Error("Provider does not support listMcpServers."));
392
- }
393
- return this.transport.request("mcpServerStatus/list", {
394
- detail: "full",
395
- ...(input?.threadId ? { threadId: input.threadId } : {}),
396
- });
397
- }
398
-
399
- startMcpOAuth(input: { serverName: string }): Promise<unknown> {
400
- if (this.protocol !== "codex-app-server") {
401
- return Promise.reject(new Error("Provider does not support startMcpOAuth."));
402
- }
403
- return this.transport.request("mcpServer/oauth/login", { name: input.serverName });
404
- }
405
-
406
- listModels(): Promise<unknown> {
407
- if (this.protocol === "codex-app-server") {
408
- return this.transport.request("model/list", {});
409
- }
410
- return Promise.resolve(undefined);
411
- }
412
-
413
- prompt(input: {
414
- sessionId: string;
415
- content: unknown[];
416
- clientMessageId: string;
417
- model?: string;
418
- reasoningEffort?: string;
419
- permissionMode?: AgentPermissionMode;
420
- collaborationMode?: AgentCollaborationMode;
421
- cwd: string;
422
- }): Promise<unknown> {
423
- if (this.protocol === "codex-app-server") {
424
- const model = input.model?.trim() || "default";
425
- const collaborationSettings = {
426
- model,
427
- ...(input.reasoningEffort ? { reasoning_effort: input.reasoningEffort } : {}),
428
- };
429
- const collaborationMode = input.collaborationMode && input.collaborationMode !== "default"
430
- ? {
431
- mode: input.collaborationMode,
432
- settings: collaborationSettings,
433
- }
434
- : undefined;
435
- const permission = permissionOverridesForMode(input.permissionMode);
436
- const approvalPolicy = approvalPolicyForMode(input.permissionMode);
437
- const turnStartParams = {
438
- threadId: input.sessionId,
439
- model,
440
- effort: input.reasoningEffort,
441
- // Named profile when we have one; sandboxPolicy only for full_access.
442
- // Omitted entirely when no mode is set so Codex uses config.toml.
443
- ...permission,
444
- ...(approvalPolicy ? { approvalPolicy } : {}),
445
- collaborationMode,
446
- input: input.content.map((block) => {
447
- const raw = block as { type?: string; text?: string; data?: string };
448
- if (raw.type === "image" && raw.data) {
449
- return { type: "image", url: raw.data };
450
- }
451
- return { type: "text", text: raw.text ?? "" };
452
- }),
453
- };
454
- return this.transport.request("turn/start", turnStartParams, null);
455
- }
456
- return this.transport.request("session/prompt", {
457
- sessionId: input.sessionId,
458
- prompt: input.content,
459
- _meta: {
460
- linkshellClientMessageId: input.clientMessageId,
461
- model: input.model,
462
- reasoningEffort: input.reasoningEffort,
463
- permissionMode: input.permissionMode,
464
- },
465
- }, 60_000);
466
- }
467
-
468
- steer(input: {
469
- sessionId: string;
470
- turnId: string;
471
- content: unknown[];
472
- }): Promise<unknown> {
473
- if (this.protocol !== "codex-app-server") {
474
- return Promise.reject(new Error("Active-turn steering is only supported by Codex app-server."));
475
- }
476
- return this.transport.request("turn/steer", {
477
- threadId: input.sessionId,
478
- expectedTurnId: input.turnId,
479
- input: input.content.map((block) => {
480
- const raw = block as { type?: string; text?: string; data?: string };
481
- if (raw.type === "image" && raw.data) {
482
- return { type: "image", url: raw.data };
483
- }
484
- return { type: "text", text: raw.text ?? "" };
485
- }),
486
- }, null);
487
- }
488
-
489
- cancel(input: { sessionId?: string; turnId?: string }): void {
490
- if (this.protocol === "codex-app-server") {
491
- if (!input.sessionId || !input.turnId) return;
492
- this.transport.request("turn/interrupt", {
493
- threadId: input.sessionId,
494
- turnId: input.turnId,
495
- }).catch(() => {});
496
- return;
497
- }
498
- if (!this.advertised.cancel) return;
499
- this.transport.notify("session/cancel", { sessionId: input.sessionId });
500
- }
501
-
502
- respondPermission(input: {
503
- sessionId?: string;
504
- requestId: string;
505
- outcome: "allow" | "deny";
506
- optionId?: string;
507
- }): void {
508
- this.transport.notify("session/respond_permission", input);
509
- }
510
-
511
- compact(input: { sessionId: string }): Promise<unknown> {
512
- if (this.protocol === "codex-app-server") {
513
- return this.transport.request("thread/compact/start", { threadId: input.sessionId });
514
- }
515
- return Promise.reject(new Error("Native compact is not supported by this provider."));
516
- }
517
-
518
- stop(): void {
519
- this.transport.stop();
520
- }
521
- }