apex-code 0.0.5 → 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.
Files changed (257) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/README.md +7 -8
  3. package/dist/cli/agent-lifecycle.d.ts +24 -0
  4. package/dist/cli/agent-lifecycle.d.ts.map +1 -0
  5. package/dist/cli/agent-lifecycle.js +126 -0
  6. package/dist/cli/agent-lifecycle.js.map +1 -0
  7. package/dist/cli/args.d.ts +13 -12
  8. package/dist/cli/args.d.ts.map +1 -1
  9. package/dist/cli/args.js +80 -30
  10. package/dist/cli/args.js.map +1 -1
  11. package/dist/cli.d.ts.map +1 -1
  12. package/dist/cli.js +4 -130
  13. package/dist/cli.js.map +1 -1
  14. package/dist/config.d.ts +1 -1
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/config.js +1 -1
  17. package/dist/config.js.map +1 -1
  18. package/dist/core/agent-session-services.d.ts.map +1 -1
  19. package/dist/core/agent-session-services.js +0 -8
  20. package/dist/core/agent-session-services.js.map +1 -1
  21. package/dist/core/agent-session.d.ts +160 -0
  22. package/dist/core/agent-session.d.ts.map +1 -1
  23. package/dist/core/agent-session.js +265 -0
  24. package/dist/core/agent-session.js.map +1 -1
  25. package/dist/core/delegation/agents.d.ts.map +1 -1
  26. package/dist/core/delegation/agents.js +2 -1
  27. package/dist/core/delegation/agents.js.map +1 -1
  28. package/dist/core/delegation/runtime.d.ts +674 -1
  29. package/dist/core/delegation/runtime.d.ts.map +1 -1
  30. package/dist/core/delegation/runtime.js +1338 -66
  31. package/dist/core/delegation/runtime.js.map +1 -1
  32. package/dist/core/extensions/loader.d.ts.map +1 -1
  33. package/dist/core/extensions/loader.js +3 -4
  34. package/dist/core/extensions/loader.js.map +1 -1
  35. package/dist/core/extensions/source-runtime.d.ts +4 -5
  36. package/dist/core/extensions/source-runtime.d.ts.map +1 -1
  37. package/dist/core/extensions/source-runtime.js +4 -5
  38. package/dist/core/extensions/source-runtime.js.map +1 -1
  39. package/dist/core/formatter-lifecycle.d.ts +2 -1
  40. package/dist/core/formatter-lifecycle.d.ts.map +1 -1
  41. package/dist/core/formatter-lifecycle.js +2 -1
  42. package/dist/core/formatter-lifecycle.js.map +1 -1
  43. package/dist/core/hooks/types.d.ts +2 -3
  44. package/dist/core/hooks/types.d.ts.map +1 -1
  45. package/dist/core/hooks/types.js +2 -3
  46. package/dist/core/hooks/types.js.map +1 -1
  47. package/dist/core/http-idle-timeout.d.ts +3 -5
  48. package/dist/core/http-idle-timeout.d.ts.map +1 -1
  49. package/dist/core/http-idle-timeout.js +3 -5
  50. package/dist/core/http-idle-timeout.js.map +1 -1
  51. package/dist/core/mcp/config.d.ts +18 -1
  52. package/dist/core/mcp/config.d.ts.map +1 -1
  53. package/dist/core/mcp/config.js +24 -7
  54. package/dist/core/mcp/config.js.map +1 -1
  55. package/dist/core/mcp/connector.d.ts +2 -3
  56. package/dist/core/mcp/connector.d.ts.map +1 -1
  57. package/dist/core/mcp/connector.js.map +1 -1
  58. package/dist/core/mcp/oauth/authorize.d.ts +2 -0
  59. package/dist/core/mcp/oauth/authorize.d.ts.map +1 -1
  60. package/dist/core/mcp/oauth/authorize.js +5 -2
  61. package/dist/core/mcp/oauth/authorize.js.map +1 -1
  62. package/dist/core/mcp/oauth/mcp-token.d.ts +4 -6
  63. package/dist/core/mcp/oauth/mcp-token.d.ts.map +1 -1
  64. package/dist/core/mcp/oauth/mcp-token.js +5 -7
  65. package/dist/core/mcp/oauth/mcp-token.js.map +1 -1
  66. package/dist/core/mcp/runtime.d.ts +1 -0
  67. package/dist/core/mcp/runtime.d.ts.map +1 -1
  68. package/dist/core/mcp/runtime.js +2 -1
  69. package/dist/core/mcp/runtime.js.map +1 -1
  70. package/dist/core/package-manager.d.ts.map +1 -1
  71. package/dist/core/package-manager.js +0 -15
  72. package/dist/core/package-manager.js.map +1 -1
  73. package/dist/core/permissions/store.d.ts +6 -11
  74. package/dist/core/permissions/store.d.ts.map +1 -1
  75. package/dist/core/permissions/store.js +23 -51
  76. package/dist/core/permissions/store.js.map +1 -1
  77. package/dist/core/project-resources.d.ts +62 -0
  78. package/dist/core/project-resources.d.ts.map +1 -0
  79. package/dist/core/project-resources.js +42 -0
  80. package/dist/core/project-resources.js.map +1 -0
  81. package/dist/core/sdk.d.ts +62 -12
  82. package/dist/core/sdk.d.ts.map +1 -1
  83. package/dist/core/sdk.js +313 -44
  84. package/dist/core/sdk.js.map +1 -1
  85. package/dist/core/session-share.d.ts +6 -8
  86. package/dist/core/session-share.d.ts.map +1 -1
  87. package/dist/core/session-share.js +9 -12
  88. package/dist/core/session-share.js.map +1 -1
  89. package/dist/core/settings-manager.d.ts +0 -21
  90. package/dist/core/settings-manager.d.ts.map +1 -1
  91. package/dist/core/settings-manager.js +4 -12
  92. package/dist/core/settings-manager.js.map +1 -1
  93. package/dist/core/tools/bash.d.ts.map +1 -1
  94. package/dist/core/tools/bash.js +0 -47
  95. package/dist/core/tools/bash.js.map +1 -1
  96. package/dist/core/tools/delegate.d.ts +8 -0
  97. package/dist/core/tools/delegate.d.ts.map +1 -1
  98. package/dist/core/tools/delegate.js +33 -3
  99. package/dist/core/tools/delegate.js.map +1 -1
  100. package/dist/core/tools/web-fetch.d.ts.map +1 -1
  101. package/dist/core/tools/web-fetch.js +4 -8
  102. package/dist/core/tools/web-fetch.js.map +1 -1
  103. package/dist/core/tools/web-search-exa.d.ts +3 -6
  104. package/dist/core/tools/web-search-exa.d.ts.map +1 -1
  105. package/dist/core/tools/web-search-exa.js +5 -8
  106. package/dist/core/tools/web-search-exa.js.map +1 -1
  107. package/dist/core/trust-manager.d.ts +6 -5
  108. package/dist/core/trust-manager.d.ts.map +1 -1
  109. package/dist/core/trust-manager.js +42 -19
  110. package/dist/core/trust-manager.js.map +1 -1
  111. package/dist/core/web-search-provider.d.ts +0 -10
  112. package/dist/core/web-search-provider.d.ts.map +1 -1
  113. package/dist/core/web-search-provider.js +2 -16
  114. package/dist/core/web-search-provider.js.map +1 -1
  115. package/dist/core/workspace/git-observer.d.ts +16 -0
  116. package/dist/core/workspace/git-observer.d.ts.map +1 -1
  117. package/dist/core/workspace/git-observer.js +8 -1
  118. package/dist/core/workspace/git-observer.js.map +1 -1
  119. package/dist/core/workspace/git-worktree-owner.d.ts +89 -0
  120. package/dist/core/workspace/git-worktree-owner.d.ts.map +1 -0
  121. package/dist/core/workspace/git-worktree-owner.js +240 -0
  122. package/dist/core/workspace/git-worktree-owner.js.map +1 -0
  123. package/dist/main.d.ts +0 -1
  124. package/dist/main.d.ts.map +1 -1
  125. package/dist/main.js +22 -3
  126. package/dist/main.js.map +1 -1
  127. package/dist/modes/acp/server.d.ts +43 -1
  128. package/dist/modes/acp/server.d.ts.map +1 -1
  129. package/dist/modes/acp/server.js +78 -0
  130. package/dist/modes/acp/server.js.map +1 -1
  131. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  132. package/dist/modes/interactive/components/settings-selector.js +1 -1
  133. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  134. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  135. package/dist/modes/interactive/interactive-mode.js +0 -29
  136. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  137. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  138. package/dist/modes/rpc/rpc-mode.js +38 -0
  139. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  140. package/dist/modes/rpc/rpc-types.d.ts +110 -0
  141. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  142. package/dist/modes/rpc/rpc-types.js.map +1 -1
  143. package/dist/utils/tools-manager.d.ts +0 -25
  144. package/dist/utils/tools-manager.d.ts.map +1 -1
  145. package/dist/utils/tools-manager.js +3 -52
  146. package/dist/utils/tools-manager.js.map +1 -1
  147. package/dist/utils/version-check.d.ts +0 -2
  148. package/dist/utils/version-check.d.ts.map +1 -1
  149. package/dist/utils/version-check.js +0 -2
  150. package/dist/utils/version-check.js.map +1 -1
  151. package/docs/settings.md +2 -7
  152. package/npm-shrinkwrap.json +5 -5
  153. package/package.json +2 -2
  154. package/dist/core/sandbox/bwrap-arguments.d.ts +0 -41
  155. package/dist/core/sandbox/bwrap-arguments.d.ts.map +0 -1
  156. package/dist/core/sandbox/bwrap-arguments.js +0 -136
  157. package/dist/core/sandbox/bwrap-arguments.js.map +0 -1
  158. package/dist/core/sandbox/child-entry.d.ts +0 -2
  159. package/dist/core/sandbox/child-entry.d.ts.map +0 -1
  160. package/dist/core/sandbox/child-entry.js +0 -24
  161. package/dist/core/sandbox/child-entry.js.map +0 -1
  162. package/dist/core/sandbox/cli-launch.d.ts +0 -162
  163. package/dist/core/sandbox/cli-launch.d.ts.map +0 -1
  164. package/dist/core/sandbox/cli-launch.js +0 -366
  165. package/dist/core/sandbox/cli-launch.js.map +0 -1
  166. package/dist/core/sandbox/cli-supervisor.d.ts +0 -31
  167. package/dist/core/sandbox/cli-supervisor.d.ts.map +0 -1
  168. package/dist/core/sandbox/cli-supervisor.js +0 -147
  169. package/dist/core/sandbox/cli-supervisor.js.map +0 -1
  170. package/dist/core/sandbox/default-hosts.d.ts +0 -28
  171. package/dist/core/sandbox/default-hosts.d.ts.map +0 -1
  172. package/dist/core/sandbox/default-hosts.js +0 -69
  173. package/dist/core/sandbox/default-hosts.js.map +0 -1
  174. package/dist/core/sandbox/full-access.d.ts +0 -28
  175. package/dist/core/sandbox/full-access.d.ts.map +0 -1
  176. package/dist/core/sandbox/full-access.js +0 -63
  177. package/dist/core/sandbox/full-access.js.map +0 -1
  178. package/dist/core/sandbox/git-identity.d.ts +0 -57
  179. package/dist/core/sandbox/git-identity.d.ts.map +0 -1
  180. package/dist/core/sandbox/git-identity.js +0 -78
  181. package/dist/core/sandbox/git-identity.js.map +0 -1
  182. package/dist/core/sandbox/host-approval.d.ts +0 -42
  183. package/dist/core/sandbox/host-approval.d.ts.map +0 -1
  184. package/dist/core/sandbox/host-approval.js +0 -99
  185. package/dist/core/sandbox/host-approval.js.map +0 -1
  186. package/dist/core/sandbox/linux-backend.d.ts +0 -48
  187. package/dist/core/sandbox/linux-backend.d.ts.map +0 -1
  188. package/dist/core/sandbox/linux-backend.js +0 -328
  189. package/dist/core/sandbox/linux-backend.js.map +0 -1
  190. package/dist/core/sandbox/macos-backend.d.ts +0 -30
  191. package/dist/core/sandbox/macos-backend.d.ts.map +0 -1
  192. package/dist/core/sandbox/macos-backend.js +0 -385
  193. package/dist/core/sandbox/macos-backend.js.map +0 -1
  194. package/dist/core/sandbox/network-proxy.d.ts +0 -38
  195. package/dist/core/sandbox/network-proxy.d.ts.map +0 -1
  196. package/dist/core/sandbox/network-proxy.js +0 -153
  197. package/dist/core/sandbox/network-proxy.js.map +0 -1
  198. package/dist/core/sandbox/network-refusal.d.ts +0 -24
  199. package/dist/core/sandbox/network-refusal.d.ts.map +0 -1
  200. package/dist/core/sandbox/network-refusal.js +0 -72
  201. package/dist/core/sandbox/network-refusal.js.map +0 -1
  202. package/dist/core/sandbox/policy.d.ts +0 -40
  203. package/dist/core/sandbox/policy.d.ts.map +0 -1
  204. package/dist/core/sandbox/policy.js +0 -55
  205. package/dist/core/sandbox/policy.js.map +0 -1
  206. package/dist/core/sandbox/profiles.d.ts +0 -25
  207. package/dist/core/sandbox/profiles.d.ts.map +0 -1
  208. package/dist/core/sandbox/profiles.js +0 -23
  209. package/dist/core/sandbox/profiles.js.map +0 -1
  210. package/dist/core/sandbox/rpc/command-client.d.ts +0 -28
  211. package/dist/core/sandbox/rpc/command-client.d.ts.map +0 -1
  212. package/dist/core/sandbox/rpc/command-client.js +0 -86
  213. package/dist/core/sandbox/rpc/command-client.js.map +0 -1
  214. package/dist/core/sandbox/rpc/command-proxy.d.ts +0 -48
  215. package/dist/core/sandbox/rpc/command-proxy.d.ts.map +0 -1
  216. package/dist/core/sandbox/rpc/command-proxy.js +0 -120
  217. package/dist/core/sandbox/rpc/command-proxy.js.map +0 -1
  218. package/dist/core/sandbox/rpc/credential-client.d.ts +0 -20
  219. package/dist/core/sandbox/rpc/credential-client.d.ts.map +0 -1
  220. package/dist/core/sandbox/rpc/credential-client.js +0 -170
  221. package/dist/core/sandbox/rpc/credential-client.js.map +0 -1
  222. package/dist/core/sandbox/rpc/credential-proxy.d.ts +0 -44
  223. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +0 -1
  224. package/dist/core/sandbox/rpc/credential-proxy.js +0 -287
  225. package/dist/core/sandbox/rpc/credential-proxy.js.map +0 -1
  226. package/dist/core/sandbox/rpc/framing.d.ts +0 -35
  227. package/dist/core/sandbox/rpc/framing.d.ts.map +0 -1
  228. package/dist/core/sandbox/rpc/framing.js +0 -114
  229. package/dist/core/sandbox/rpc/framing.js.map +0 -1
  230. package/dist/core/sandbox/rpc/git-credential-helper.d.ts +0 -47
  231. package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +0 -1
  232. package/dist/core/sandbox/rpc/git-credential-helper.js +0 -222
  233. package/dist/core/sandbox/rpc/git-credential-helper.js.map +0 -1
  234. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +0 -66
  235. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +0 -1
  236. package/dist/core/sandbox/rpc/git-credential-proxy.js +0 -209
  237. package/dist/core/sandbox/rpc/git-credential-proxy.js.map +0 -1
  238. package/dist/core/sandbox/supervisor-temp.d.ts +0 -18
  239. package/dist/core/sandbox/supervisor-temp.d.ts.map +0 -1
  240. package/dist/core/sandbox/supervisor-temp.js +0 -21
  241. package/dist/core/sandbox/supervisor-temp.js.map +0 -1
  242. package/dist/core/sandbox/supervisor.d.ts +0 -62
  243. package/dist/core/sandbox/supervisor.d.ts.map +0 -1
  244. package/dist/core/sandbox/supervisor.js +0 -32
  245. package/dist/core/sandbox/supervisor.js.map +0 -1
  246. package/dist/core/sandbox/terminal-handoff.d.ts +0 -68
  247. package/dist/core/sandbox/terminal-handoff.d.ts.map +0 -1
  248. package/dist/core/sandbox/terminal-handoff.js +0 -221
  249. package/dist/core/sandbox/terminal-handoff.js.map +0 -1
  250. package/dist/core/sandbox/terminal-size.d.ts +0 -53
  251. package/dist/core/sandbox/terminal-size.d.ts.map +0 -1
  252. package/dist/core/sandbox/terminal-size.js +0 -126
  253. package/dist/core/sandbox/terminal-size.js.map +0 -1
  254. package/dist/core/sandbox/violations.d.ts +0 -23
  255. package/dist/core/sandbox/violations.d.ts.map +0 -1
  256. package/dist/core/sandbox/violations.js +0 -30
  257. package/dist/core/sandbox/violations.js.map +0 -1
@@ -203,6 +203,84 @@ export class AcpServer {
203
203
  this.#write({ jsonrpc: "2.0", id, result: {} });
204
204
  return;
205
205
  }
206
+ // Child lifecycle parity (spec 2026-09-09, protocol section): every
207
+ // operation routes through the same AgentSession pass-throughs RPC
208
+ // calls, with the same payload shapes and the same unknown-ID
209
+ // rejection surfacing as -32000 errors. There is no second,
210
+ // host-supplied lifecycle path.
211
+ case "agent/list": {
212
+ this.#write({ jsonrpc: "2.0", id, result: this.#host.getSession().listChildRuns() });
213
+ return;
214
+ }
215
+ case "agent/send": {
216
+ if (typeof record.runId !== "string" || typeof record.input !== "string")
217
+ throw new Error("agent/send requires runId and input.");
218
+ await this.#host.getSession().sendChildInput(record.runId, record.input);
219
+ this.#write({ jsonrpc: "2.0", id, result: null });
220
+ return;
221
+ }
222
+ case "agent/spawn": {
223
+ if (typeof record.agentType !== "string" || typeof record.task !== "string")
224
+ throw new Error("agent/spawn requires agentType and task.");
225
+ const workspace = record.workspace;
226
+ const idempotencyKey = typeof record.idempotencyKey === "string" ? record.idempotencyKey : undefined;
227
+ const timeoutMs = typeof record.timeoutMs === "number" ? record.timeoutMs : undefined;
228
+ const spawned = await this.#host.getSession().startChildRun(record.agentType, record.task, {
229
+ ...(workspace ? { workspace } : {}),
230
+ ...(idempotencyKey !== undefined ? { idempotencyKey } : {}),
231
+ ...(timeoutMs !== undefined ? { timeoutMs } : {}),
232
+ });
233
+ this.#write({ jsonrpc: "2.0", id, result: spawned });
234
+ return;
235
+ }
236
+ case "agent/wait": {
237
+ if (typeof record.runId !== "string")
238
+ throw new Error("agent/wait requires runId.");
239
+ // Identical to RPC's agent/wait: the awaited latest settled turn
240
+ // with the registry status after settlement.
241
+ const waited = await this.#host.getSession().waitChildRunResult(record.runId);
242
+ this.#write({ jsonrpc: "2.0", id, result: waited });
243
+ return;
244
+ }
245
+ case "agent/status": {
246
+ if (typeof record.runId !== "string")
247
+ throw new Error("agent/status requires runId.");
248
+ // Non-blocking: the snapshot is built from the record/entry alone.
249
+ const status = this.#host.getSession().childRunStatus(record.runId);
250
+ this.#write({ jsonrpc: "2.0", id, result: status });
251
+ return;
252
+ }
253
+ case "agent/resume": {
254
+ if (typeof record.runId !== "string")
255
+ throw new Error("agent/resume requires runId.");
256
+ const input = typeof record.input === "string" ? record.input : undefined;
257
+ const status = await this.#host.getSession().resumeChildRun(record.runId, input);
258
+ this.#write({ jsonrpc: "2.0", id, result: { status } });
259
+ return;
260
+ }
261
+ case "agent/recover": {
262
+ if (typeof record.runId !== "string")
263
+ throw new Error("agent/recover requires runId.");
264
+ // Identical to RPC's agent/recover: the verified reactivation
265
+ // payload `{workspaceState, dirty}`; refusals surface as -32000.
266
+ const recovered = await this.#host.getSession().recoverChildWorkspace(record.runId);
267
+ this.#write({ jsonrpc: "2.0", id, result: recovered });
268
+ return;
269
+ }
270
+ case "agent/interrupt":
271
+ case "agent/close": {
272
+ if (typeof record.runId !== "string")
273
+ throw new Error(`${method} requires runId.`);
274
+ const session = this.#host.getSession();
275
+ if (method === "agent/interrupt") {
276
+ const reason = typeof record.reason === "string" ? record.reason : undefined;
277
+ session.interruptChildRun(record.runId, reason);
278
+ }
279
+ else
280
+ session.closeChildRun(record.runId);
281
+ this.#write({ jsonrpc: "2.0", id, result: null });
282
+ return;
283
+ }
206
284
  default:
207
285
  this.#write({ jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } });
208
286
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/modes/acp/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Readable, Writable } from \"node:stream\";\nimport type { AgentSessionRuntime } from \"../../core/agent-session-runtime.ts\";\nimport type { InputSource } from \"../../core/extensions/types.ts\";\nimport { takeOverStdout } from \"../../core/output-guard.ts\";\nimport type { PermissionAnswer } from \"../../core/permissions/responder.ts\";\nimport { attachJsonlLineReader, serializeJsonLine } from \"../rpc/jsonl.ts\";\nimport { translateJsonEvent } from \"./translate.ts\";\n\n/**\n * The ACP server (spec 2026-08-31-acp-adapter.md): an in-process ACP v1 agent\n * over newline-delimited JSON-RPC on stdio. The host adapters between the\n * protocol and the live AgentSession; the server never imports session types,\n * so tests drive it entirely through fakes.\n */\n\n/** The slice of AgentSession the protocol actually drives. */\nexport interface AcpPromptableSession {\n\tprompt(text: string, options: { source: InputSource }): Promise<unknown>;\n\tabort(): Promise<void> | void;\n}\n\nexport interface AcpHost {\n\tgetSession(): AcpPromptableSession;\n\tcreateSession(cwd: string): Promise<AcpPromptableSession>;\n\tloadSession(sessionId: string, cwd: string): Promise<AcpPromptableSession>;\n\tsetMode?(mode: string): Promise<void> | void;\n}\n\nconst PERMISSION_OPTIONS = [\n\t{ optionId: \"allow-once\", name: \"Allow once\", kind: \"allow_once\" },\n\t// `optionId` and `kind` are the wire contract; only the display name is ours to fix.\n\t// The gate writes this to `session`, so \"always\" was never true.\n\t{ optionId: \"allow-always\", name: \"Allow for this session\", kind: \"allow_always\" },\n\t{ optionId: \"reject-once\", name: \"Reject\", kind: \"reject_once\" },\n\t{ optionId: \"reject-always\", name: \"Reject always\", kind: \"reject_always\" },\n] as const;\n\nexport class AcpServer {\n\treadonly #input: Readable;\n\treadonly #output: Writable;\n\treadonly #host: AcpHost;\n\treadonly #pendingPermissions = new Map<string, (answer: PermissionAnswer) => void>();\n\treadonly #seenToolCalls = new Set<string>();\n\t#currentSessionId: string | undefined;\n\n\tconstructor(deps: { input: Readable; output: Writable; host: AcpHost }) {\n\t\tthis.#input = deps.input;\n\t\tthis.#output = deps.output;\n\t\tthis.#host = deps.host;\n\t}\n\n\tstart(): void {\n\t\tattachJsonlLineReader(this.#input, (line) => this.handleLine(line));\n\t}\n\n\tget currentSessionId(): string | undefined {\n\t\treturn this.#currentSessionId;\n\t}\n\n\thandleLine(line: string): void {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(line);\n\t\t} catch {\n\t\t\t// ACP is line-delimited JSON; anything else on the wire is dropped, not fatal.\n\t\t\treturn;\n\t\t}\n\t\tif (typeof parsed !== \"object\" || parsed === null) return;\n\t\tconst message = parsed as Record<string, unknown>;\n\t\tconst { id, method, params } = message;\n\n\t\tif (typeof method !== \"string\") {\n\t\t\t// No method: a response to one of our server->client requests (permission).\n\t\t\tif (id !== undefined) this.#resolvePermission(String(id), message.result);\n\t\t\treturn;\n\t\t}\n\t\tif (id === undefined) {\n\t\t\tif (method === \"session/cancel\") {\n\t\t\t\tvoid this.#host.getSession().abort();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tvoid this.#dispatch(id, method, params);\n\t}\n\n\t/** Entry point for live session events; translates and emits session/update. */\n\tonSessionEvent(event: unknown): void {\n\t\tif (this.#currentSessionId === undefined) return;\n\t\tconst translated = translateJsonEvent(event);\n\t\tif (!translated) return;\n\t\tif (translated.kind === \"message\") {\n\t\t\tthis.#notify(this.#currentSessionId, translated.update);\n\t\t\treturn;\n\t\t}\n\t\tconst seen = this.#seenToolCalls.has(translated.toolCallId);\n\t\tthis.#seenToolCalls.add(translated.toolCallId);\n\t\tthis.#notify(this.#currentSessionId, {\n\t\t\tsessionUpdate: seen ? \"tool_call_update\" : \"tool_call\",\n\t\t\t...translated.update,\n\t\t});\n\t}\n\n\t/**\n\t * The PermissionResponder bridge: turns one ACP permission round trip into\n\t * the gate's answer shape. Rule persistence itself stays in the gate\n\t * (ADR 0010) -- `persist` is a request, not a written rule.\n\t */\n\t/**\n\t * `canPersistSession` mirrors the gate's `sessionScope`. When the tool's\n\t * `ruleForCall()` yields nothing, the gate has no rule to write, so neither\n\t * standing choice can keep its promise and neither is offered.\n\t */\n\taskPermission(\n\t\tsessionId: string,\n\t\ttoolName: string,\n\t\tdescription: string,\n\t\tcanPersistSession = true,\n\t): Promise<PermissionAnswer> {\n\t\tconst id = `perm_${randomUUID()}`;\n\t\treturn new Promise<PermissionAnswer>((resolve) => {\n\t\t\tthis.#pendingPermissions.set(id, resolve);\n\t\t\tthis.#write({\n\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\tid,\n\t\t\t\tmethod: \"session/request_permission\",\n\t\t\t\tparams: {\n\t\t\t\t\tsessionId,\n\t\t\t\t\ttoolCall: { toolCallId: id, title: `${toolName}: ${description}`, kind: \"other\", status: \"pending\" },\n\t\t\t\t\t// Both standing choices need a rule to stand on. Offering either when\n\t\t\t\t\t// ruleForCall() yields nothing promises a decision that outlives the\n\t\t\t\t\t// call and then does not.\n\t\t\t\t\toptions: canPersistSession\n\t\t\t\t\t\t? PERMISSION_OPTIONS\n\t\t\t\t\t\t: PERMISSION_OPTIONS.filter(\n\t\t\t\t\t\t\t\t(option) => option.kind !== \"allow_always\" && option.kind !== \"reject_always\",\n\t\t\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\t#write(value: unknown): void {\n\t\tthis.#output.write(serializeJsonLine(value));\n\t}\n\n\t#notify(sessionId: string, update: Record<string, unknown>): void {\n\t\tthis.#write({ jsonrpc: \"2.0\", method: \"session/update\", params: { sessionId, update } });\n\t}\n\n\t#resolvePermission(id: string, result: unknown): void {\n\t\tconst resolve = this.#pendingPermissions.get(id);\n\t\tif (!resolve) return;\n\t\tthis.#pendingPermissions.delete(id);\n\t\tconst outcome = (result as { outcome?: { outcome?: string; optionId?: string } } | undefined)?.outcome;\n\t\tif (outcome?.outcome !== \"selected\" || typeof outcome.optionId !== \"string\") {\n\t\t\t// Cancelled, malformed, or unknown -- fail closed.\n\t\t\tresolve({ allow: false });\n\t\t\treturn;\n\t\t}\n\t\tif (outcome.optionId.startsWith(\"allow\")) {\n\t\t\tresolve({ allow: true, persist: outcome.optionId === \"allow-always\" });\n\t\t\treturn;\n\t\t}\n\t\tresolve({ allow: false, persist: outcome.optionId === \"reject-always\" });\n\t}\n\n\tasync #dispatch(id: unknown, method: string, params: unknown): Promise<void> {\n\t\tconst record = (typeof params === \"object\" && params !== null ? params : {}) as Record<string, unknown>;\n\t\ttry {\n\t\t\tswitch (method) {\n\t\t\t\tcase \"initialize\": {\n\t\t\t\t\tthis.#write({\n\t\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tresult: {\n\t\t\t\t\t\t\tprotocolVersion: 1,\n\t\t\t\t\t\t\tagentCapabilities: { loadSession: true },\n\t\t\t\t\t\t\tpromptCapabilities: { image: false, audio: false, embeddedContext: false },\n\t\t\t\t\t\t\tagentInfo: { name: \"apex-code\", title: \"Apex Code\" },\n\t\t\t\t\t\t\tauthMethods: [],\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"session/new\": {\n\t\t\t\t\tconst cwd = typeof record.cwd === \"string\" ? record.cwd : process.cwd();\n\t\t\t\t\t// The host rebinds its live session; the returned session is driven\n\t\t\t\t\t// through host.getSession() afterwards.\n\t\t\t\t\tawait this.#host.createSession(cwd);\n\t\t\t\t\tthis.#currentSessionId = `sess_${randomUUID()}`;\n\t\t\t\t\tthis.#seenToolCalls.clear();\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: { sessionId: this.#currentSessionId } });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"session/load\": {\n\t\t\t\t\tconst sessionId = typeof record.sessionId === \"string\" ? record.sessionId : \"\";\n\t\t\t\t\tconst cwd = typeof record.cwd === \"string\" ? record.cwd : process.cwd();\n\t\t\t\t\tawait this.#host.loadSession(sessionId, cwd);\n\t\t\t\t\tthis.#currentSessionId = sessionId;\n\t\t\t\t\tthis.#seenToolCalls.clear();\n\t\t\t\t\t// History replay is deferred (plan, ACP.2): the session reattaches\n\t\t\t\t\t// and continues, but prior entries are not streamed in v1.\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: null });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"session/prompt\": {\n\t\t\t\t\tif (this.#currentSessionId === undefined || record.sessionId !== this.#currentSessionId) {\n\t\t\t\t\t\tthis.#write({\n\t\t\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\terror: { code: -32002, message: \"Unknown session; call session/new first\" },\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst prompt = Array.isArray(record.prompt) ? record.prompt : [];\n\t\t\t\t\tconst text = prompt\n\t\t\t\t\t\t.filter((block): block is { type: \"text\"; text: string } => {\n\t\t\t\t\t\t\tconst candidate = block as { type?: string; text?: string };\n\t\t\t\t\t\t\treturn candidate.type === \"text\" && typeof candidate.text === \"string\";\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.map((block) => block.text)\n\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.#host.getSession().prompt(text, { source: \"acp\" });\n\t\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: { stopReason: \"end_turn\" } });\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst messageText = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\tif (/abort/i.test(messageText)) {\n\t\t\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: { stopReason: \"cancelled\" } });\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"session/set_mode\": {\n\t\t\t\t\tconst mode = typeof record.mode === \"string\" ? record.mode : \"\";\n\t\t\t\t\tawait this.#host.setMode?.(mode);\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, error: { code: -32601, message: `Method not found: ${method}` } });\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.#write({\n\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\tid,\n\t\t\t\terror: { code: -32000, message: error instanceof Error ? error.message : String(error) },\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Run in ACP mode: ACP v1 agent over stdio for the runtime's session. ACP owns\n * stdout from here on (output guard takes it over).\n */\nexport async function runAcpMode(runtime: AgentSessionRuntime, bridge?: { current?: AcpServer }): Promise<never> {\n\ttakeOverStdout();\n\tconst host: AcpHost = {\n\t\tgetSession: () => runtime.session,\n\t\tcreateSession: async () => {\n\t\t\tawait runtime.newSession();\n\t\t\treturn runtime.session;\n\t\t},\n\t\tloadSession: async () => runtime.session,\n\t\tsetMode: async (mode) => {\n\t\t\tawait runtime.session.setPermissionMode(mode as Parameters<typeof runtime.session.setPermissionMode>[0]);\n\t\t},\n\t};\n\tconst server = new AcpServer({ input: process.stdin, output: process.stdout, host });\n\tif (bridge) bridge.current = server;\n\truntime.session.subscribe((event) => server.onSessionEvent(event));\n\tserver.start();\n\t// The ACP connection owns the process lifetime; it ends when stdin closes.\n\treturn new Promise(() => {});\n}\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/modes/acp/server.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Readable, Writable } from \"node:stream\";\nimport type { AgentSessionRuntime } from \"../../core/agent-session-runtime.ts\";\nimport type {\n\tChildRunPolicySnapshot,\n\tChildRunStatus,\n\tChildTurnOutcome,\n\tChildWorkspaceRequest,\n\tChildWorkspaceState,\n} from \"../../core/delegation/runtime.ts\";\nimport type { InputSource } from \"../../core/extensions/types.ts\";\nimport { takeOverStdout } from \"../../core/output-guard.ts\";\nimport type { PermissionAnswer } from \"../../core/permissions/responder.ts\";\nimport { attachJsonlLineReader, serializeJsonLine } from \"../rpc/jsonl.ts\";\nimport { translateJsonEvent } from \"./translate.ts\";\n\n/**\n * The ACP server (spec 2026-08-31-acp-adapter.md): an in-process ACP v1 agent\n * over newline-delimited JSON-RPC on stdio. The host adapters between the\n * protocol and the live AgentSession; the server never imports session types,\n * so tests drive it entirely through fakes.\n */\n\n/** The slice of AgentSession the protocol actually drives, including the child lifecycle pass-throughs RPC calls. */\nexport interface AcpPromptableSession {\n\tprompt(text: string, options: { source: InputSource }): Promise<unknown>;\n\tabort(): Promise<void> | void;\n\tlistChildRuns(): unknown[];\n\tstartChildRun(\n\t\tagentType: string,\n\t\ttask: string,\n\t\trequest?: { workspace?: ChildWorkspaceRequest; idempotencyKey?: string; timeoutMs?: number },\n\t): Promise<{ handleId: string; created: boolean }>;\n\twaitChildRun(runId: string): Promise<unknown>;\n\t/**\n\t * The awaited turn's payload with the registry status after settlement, plus\n\t * the run's additive linkage fields (`artifactDir`, `sessionFile`,\n\t * `parentSessionId`, `policy`, `sandboxEnforced`) when the registry snapshot\n\t * knows them -- identical to RPC's `agent/wait` payload.\n\t */\n\twaitChildRunResult(runId: string): Promise<{\n\t\tstatus: string;\n\t\toutput: string;\n\t\toutcome: ChildTurnOutcome;\n\t\tartifactDir?: string;\n\t\tsessionFile?: string;\n\t\tparentSessionId?: string;\n\t\tpolicy?: ChildRunPolicySnapshot;\n\t\tsandboxEnforced?: boolean;\n\t}>;\n\t/** Non-blocking status snapshot for one child run (`agent/status`). */\n\tchildRunStatus(runId: string): ChildRunStatus;\n\t/**\n\t * Explicit recovery of a retained child worktree (`agent/recover`), the\n\t * pass-through `AgentSession.recoverChildWorkspace` calls. Identical payload\n\t * to RPC's `agent/recover`: `{workspaceState: \"active\", dirty}`.\n\t */\n\trecoverChildWorkspace(runId: string): Promise<{ workspaceState: ChildWorkspaceState; dirty: boolean }>;\n\tsendChildInput(runId: string, input: string): Promise<void>;\n\tresumeChildRun(runId: string, input?: string): Promise<string>;\n\tinterruptChildRun(runId: string, reason?: string): void;\n\tcloseChildRun(runId: string): void;\n}\n\nexport interface AcpHost {\n\tgetSession(): AcpPromptableSession;\n\tcreateSession(cwd: string): Promise<AcpPromptableSession>;\n\tloadSession(sessionId: string, cwd: string): Promise<AcpPromptableSession>;\n\tsetMode?(mode: string): Promise<void> | void;\n}\n\nconst PERMISSION_OPTIONS = [\n\t{ optionId: \"allow-once\", name: \"Allow once\", kind: \"allow_once\" },\n\t// `optionId` and `kind` are the wire contract; only the display name is ours to fix.\n\t// The gate writes this to `session`, so \"always\" was never true.\n\t{ optionId: \"allow-always\", name: \"Allow for this session\", kind: \"allow_always\" },\n\t{ optionId: \"reject-once\", name: \"Reject\", kind: \"reject_once\" },\n\t{ optionId: \"reject-always\", name: \"Reject always\", kind: \"reject_always\" },\n] as const;\n\nexport class AcpServer {\n\treadonly #input: Readable;\n\treadonly #output: Writable;\n\treadonly #host: AcpHost;\n\treadonly #pendingPermissions = new Map<string, (answer: PermissionAnswer) => void>();\n\treadonly #seenToolCalls = new Set<string>();\n\t#currentSessionId: string | undefined;\n\n\tconstructor(deps: { input: Readable; output: Writable; host: AcpHost }) {\n\t\tthis.#input = deps.input;\n\t\tthis.#output = deps.output;\n\t\tthis.#host = deps.host;\n\t}\n\n\tstart(): void {\n\t\tattachJsonlLineReader(this.#input, (line) => this.handleLine(line));\n\t}\n\n\tget currentSessionId(): string | undefined {\n\t\treturn this.#currentSessionId;\n\t}\n\n\thandleLine(line: string): void {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(line);\n\t\t} catch {\n\t\t\t// ACP is line-delimited JSON; anything else on the wire is dropped, not fatal.\n\t\t\treturn;\n\t\t}\n\t\tif (typeof parsed !== \"object\" || parsed === null) return;\n\t\tconst message = parsed as Record<string, unknown>;\n\t\tconst { id, method, params } = message;\n\n\t\tif (typeof method !== \"string\") {\n\t\t\t// No method: a response to one of our server->client requests (permission).\n\t\t\tif (id !== undefined) this.#resolvePermission(String(id), message.result);\n\t\t\treturn;\n\t\t}\n\t\tif (id === undefined) {\n\t\t\tif (method === \"session/cancel\") {\n\t\t\t\tvoid this.#host.getSession().abort();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tvoid this.#dispatch(id, method, params);\n\t}\n\n\t/** Entry point for live session events; translates and emits session/update. */\n\tonSessionEvent(event: unknown): void {\n\t\tif (this.#currentSessionId === undefined) return;\n\t\tconst translated = translateJsonEvent(event);\n\t\tif (!translated) return;\n\t\tif (translated.kind === \"message\") {\n\t\t\tthis.#notify(this.#currentSessionId, translated.update);\n\t\t\treturn;\n\t\t}\n\t\tconst seen = this.#seenToolCalls.has(translated.toolCallId);\n\t\tthis.#seenToolCalls.add(translated.toolCallId);\n\t\tthis.#notify(this.#currentSessionId, {\n\t\t\tsessionUpdate: seen ? \"tool_call_update\" : \"tool_call\",\n\t\t\t...translated.update,\n\t\t});\n\t}\n\n\t/**\n\t * The PermissionResponder bridge: turns one ACP permission round trip into\n\t * the gate's answer shape. Rule persistence itself stays in the gate\n\t * (ADR 0010) -- `persist` is a request, not a written rule.\n\t */\n\t/**\n\t * `canPersistSession` mirrors the gate's `sessionScope`. When the tool's\n\t * `ruleForCall()` yields nothing, the gate has no rule to write, so neither\n\t * standing choice can keep its promise and neither is offered.\n\t */\n\taskPermission(\n\t\tsessionId: string,\n\t\ttoolName: string,\n\t\tdescription: string,\n\t\tcanPersistSession = true,\n\t): Promise<PermissionAnswer> {\n\t\tconst id = `perm_${randomUUID()}`;\n\t\treturn new Promise<PermissionAnswer>((resolve) => {\n\t\t\tthis.#pendingPermissions.set(id, resolve);\n\t\t\tthis.#write({\n\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\tid,\n\t\t\t\tmethod: \"session/request_permission\",\n\t\t\t\tparams: {\n\t\t\t\t\tsessionId,\n\t\t\t\t\ttoolCall: { toolCallId: id, title: `${toolName}: ${description}`, kind: \"other\", status: \"pending\" },\n\t\t\t\t\t// Both standing choices need a rule to stand on. Offering either when\n\t\t\t\t\t// ruleForCall() yields nothing promises a decision that outlives the\n\t\t\t\t\t// call and then does not.\n\t\t\t\t\toptions: canPersistSession\n\t\t\t\t\t\t? PERMISSION_OPTIONS\n\t\t\t\t\t\t: PERMISSION_OPTIONS.filter(\n\t\t\t\t\t\t\t\t(option) => option.kind !== \"allow_always\" && option.kind !== \"reject_always\",\n\t\t\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t});\n\t\t});\n\t}\n\n\t#write(value: unknown): void {\n\t\tthis.#output.write(serializeJsonLine(value));\n\t}\n\n\t#notify(sessionId: string, update: Record<string, unknown>): void {\n\t\tthis.#write({ jsonrpc: \"2.0\", method: \"session/update\", params: { sessionId, update } });\n\t}\n\n\t#resolvePermission(id: string, result: unknown): void {\n\t\tconst resolve = this.#pendingPermissions.get(id);\n\t\tif (!resolve) return;\n\t\tthis.#pendingPermissions.delete(id);\n\t\tconst outcome = (result as { outcome?: { outcome?: string; optionId?: string } } | undefined)?.outcome;\n\t\tif (outcome?.outcome !== \"selected\" || typeof outcome.optionId !== \"string\") {\n\t\t\t// Cancelled, malformed, or unknown -- fail closed.\n\t\t\tresolve({ allow: false });\n\t\t\treturn;\n\t\t}\n\t\tif (outcome.optionId.startsWith(\"allow\")) {\n\t\t\tresolve({ allow: true, persist: outcome.optionId === \"allow-always\" });\n\t\t\treturn;\n\t\t}\n\t\tresolve({ allow: false, persist: outcome.optionId === \"reject-always\" });\n\t}\n\n\tasync #dispatch(id: unknown, method: string, params: unknown): Promise<void> {\n\t\tconst record = (typeof params === \"object\" && params !== null ? params : {}) as Record<string, unknown>;\n\t\ttry {\n\t\t\tswitch (method) {\n\t\t\t\tcase \"initialize\": {\n\t\t\t\t\tthis.#write({\n\t\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tresult: {\n\t\t\t\t\t\t\tprotocolVersion: 1,\n\t\t\t\t\t\t\tagentCapabilities: { loadSession: true },\n\t\t\t\t\t\t\tpromptCapabilities: { image: false, audio: false, embeddedContext: false },\n\t\t\t\t\t\t\tagentInfo: { name: \"apex-code\", title: \"Apex Code\" },\n\t\t\t\t\t\t\tauthMethods: [],\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"session/new\": {\n\t\t\t\t\tconst cwd = typeof record.cwd === \"string\" ? record.cwd : process.cwd();\n\t\t\t\t\t// The host rebinds its live session; the returned session is driven\n\t\t\t\t\t// through host.getSession() afterwards.\n\t\t\t\t\tawait this.#host.createSession(cwd);\n\t\t\t\t\tthis.#currentSessionId = `sess_${randomUUID()}`;\n\t\t\t\t\tthis.#seenToolCalls.clear();\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: { sessionId: this.#currentSessionId } });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"session/load\": {\n\t\t\t\t\tconst sessionId = typeof record.sessionId === \"string\" ? record.sessionId : \"\";\n\t\t\t\t\tconst cwd = typeof record.cwd === \"string\" ? record.cwd : process.cwd();\n\t\t\t\t\tawait this.#host.loadSession(sessionId, cwd);\n\t\t\t\t\tthis.#currentSessionId = sessionId;\n\t\t\t\t\tthis.#seenToolCalls.clear();\n\t\t\t\t\t// History replay is deferred (plan, ACP.2): the session reattaches\n\t\t\t\t\t// and continues, but prior entries are not streamed in v1.\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: null });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"session/prompt\": {\n\t\t\t\t\tif (this.#currentSessionId === undefined || record.sessionId !== this.#currentSessionId) {\n\t\t\t\t\t\tthis.#write({\n\t\t\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\terror: { code: -32002, message: \"Unknown session; call session/new first\" },\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst prompt = Array.isArray(record.prompt) ? record.prompt : [];\n\t\t\t\t\tconst text = prompt\n\t\t\t\t\t\t.filter((block): block is { type: \"text\"; text: string } => {\n\t\t\t\t\t\t\tconst candidate = block as { type?: string; text?: string };\n\t\t\t\t\t\t\treturn candidate.type === \"text\" && typeof candidate.text === \"string\";\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.map((block) => block.text)\n\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.#host.getSession().prompt(text, { source: \"acp\" });\n\t\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: { stopReason: \"end_turn\" } });\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst messageText = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\tif (/abort/i.test(messageText)) {\n\t\t\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: { stopReason: \"cancelled\" } });\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"session/set_mode\": {\n\t\t\t\t\tconst mode = typeof record.mode === \"string\" ? record.mode : \"\";\n\t\t\t\t\tawait this.#host.setMode?.(mode);\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// Child lifecycle parity (spec 2026-09-09, protocol section): every\n\t\t\t\t// operation routes through the same AgentSession pass-throughs RPC\n\t\t\t\t// calls, with the same payload shapes and the same unknown-ID\n\t\t\t\t// rejection surfacing as -32000 errors. There is no second,\n\t\t\t\t// host-supplied lifecycle path.\n\t\t\t\tcase \"agent/list\": {\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: this.#host.getSession().listChildRuns() });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"agent/send\": {\n\t\t\t\t\tif (typeof record.runId !== \"string\" || typeof record.input !== \"string\")\n\t\t\t\t\t\tthrow new Error(\"agent/send requires runId and input.\");\n\t\t\t\t\tawait this.#host.getSession().sendChildInput(record.runId, record.input);\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: null });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"agent/spawn\": {\n\t\t\t\t\tif (typeof record.agentType !== \"string\" || typeof record.task !== \"string\")\n\t\t\t\t\t\tthrow new Error(\"agent/spawn requires agentType and task.\");\n\t\t\t\t\tconst workspace = record.workspace as ChildWorkspaceRequest | undefined;\n\t\t\t\t\tconst idempotencyKey = typeof record.idempotencyKey === \"string\" ? record.idempotencyKey : undefined;\n\t\t\t\t\tconst timeoutMs = typeof record.timeoutMs === \"number\" ? record.timeoutMs : undefined;\n\t\t\t\t\tconst spawned = await this.#host.getSession().startChildRun(record.agentType, record.task, {\n\t\t\t\t\t\t...(workspace ? { workspace } : {}),\n\t\t\t\t\t\t...(idempotencyKey !== undefined ? { idempotencyKey } : {}),\n\t\t\t\t\t\t...(timeoutMs !== undefined ? { timeoutMs } : {}),\n\t\t\t\t\t});\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: spawned });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"agent/wait\": {\n\t\t\t\t\tif (typeof record.runId !== \"string\") throw new Error(\"agent/wait requires runId.\");\n\t\t\t\t\t// Identical to RPC's agent/wait: the awaited latest settled turn\n\t\t\t\t\t// with the registry status after settlement.\n\t\t\t\t\tconst waited = await this.#host.getSession().waitChildRunResult(record.runId);\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: waited });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"agent/status\": {\n\t\t\t\t\tif (typeof record.runId !== \"string\") throw new Error(\"agent/status requires runId.\");\n\t\t\t\t\t// Non-blocking: the snapshot is built from the record/entry alone.\n\t\t\t\t\tconst status = this.#host.getSession().childRunStatus(record.runId);\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: status });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"agent/resume\": {\n\t\t\t\t\tif (typeof record.runId !== \"string\") throw new Error(\"agent/resume requires runId.\");\n\t\t\t\t\tconst input = typeof record.input === \"string\" ? record.input : undefined;\n\t\t\t\t\tconst status = await this.#host.getSession().resumeChildRun(record.runId, input);\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: { status } });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"agent/recover\": {\n\t\t\t\t\tif (typeof record.runId !== \"string\") throw new Error(\"agent/recover requires runId.\");\n\t\t\t\t\t// Identical to RPC's agent/recover: the verified reactivation\n\t\t\t\t\t// payload `{workspaceState, dirty}`; refusals surface as -32000.\n\t\t\t\t\tconst recovered = await this.#host.getSession().recoverChildWorkspace(record.runId);\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: recovered });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcase \"agent/interrupt\":\n\t\t\t\tcase \"agent/close\": {\n\t\t\t\t\tif (typeof record.runId !== \"string\") throw new Error(`${method} requires runId.`);\n\t\t\t\t\tconst session = this.#host.getSession();\n\t\t\t\t\tif (method === \"agent/interrupt\") {\n\t\t\t\t\t\tconst reason = typeof record.reason === \"string\" ? record.reason : undefined;\n\t\t\t\t\t\tsession.interruptChildRun(record.runId, reason);\n\t\t\t\t\t} else session.closeChildRun(record.runId);\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, result: null });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tthis.#write({ jsonrpc: \"2.0\", id, error: { code: -32601, message: `Method not found: ${method}` } });\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.#write({\n\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\tid,\n\t\t\t\terror: { code: -32000, message: error instanceof Error ? error.message : String(error) },\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Run in ACP mode: ACP v1 agent over stdio for the runtime's session. ACP owns\n * stdout from here on (output guard takes it over).\n */\nexport async function runAcpMode(runtime: AgentSessionRuntime, bridge?: { current?: AcpServer }): Promise<never> {\n\ttakeOverStdout();\n\tconst host: AcpHost = {\n\t\tgetSession: () => runtime.session,\n\t\tcreateSession: async () => {\n\t\t\tawait runtime.newSession();\n\t\t\treturn runtime.session;\n\t\t},\n\t\tloadSession: async () => runtime.session,\n\t\tsetMode: async (mode) => {\n\t\t\tawait runtime.session.setPermissionMode(mode as Parameters<typeof runtime.session.setPermissionMode>[0]);\n\t\t},\n\t};\n\tconst server = new AcpServer({ input: process.stdin, output: process.stdout, host });\n\tif (bridge) bridge.current = server;\n\truntime.session.subscribe((event) => server.onSessionEvent(event));\n\tserver.start();\n\t// The ACP connection owns the process lifetime; it ends when stdin closes.\n\treturn new Promise(() => {});\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"settings-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/settings-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { getSupportedThinkingLevels, type Model, type Transport } from \"@earendil-works/pi-ai\";\nimport {\n\ttype Component,\n\tContainer,\n\tgetCapabilities,\n\ttype ScrollViewScrollbar,\n\ttype SelectItem,\n\ttype SettingItem,\n\tSettingsList,\n\tSpacer,\n\tText,\n} from \"@earendil-works/pi-tui\";\nimport type { ThinkingLevel } from \"apex-code-agent-core\";\nimport { formatHttpIdleTimeoutMs, HTTP_IDLE_TIMEOUT_CHOICES } from \"../../../core/http-dispatcher.ts\";\nimport type { PermissionModeOrigin } from \"../../../core/permissions/startup.ts\";\nimport { PERMISSION_MODES, type PermissionMode } from \"../../../core/permissions/store.ts\";\nimport type {\n\tDefaultProjectTrust,\n\tFullscreenExitOutput,\n\tMermaidRenderingMode,\n\tTuiMode,\n\tWarningSettings,\n} from \"../../../core/settings-manager.ts\";\nimport { getSettingsListTheme, parseAutoThemeSetting, type TerminalTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyDisplayText } from \"./keybinding-hints.ts\";\nimport { SelectSubmenu, SteppedSubmenu, type SteppedSubmenuStep } from \"./settings-submenu.ts\";\n\nconst MODEL_PICKER_LAYOUT = { minPrimaryColumnWidth: 12, maxPrimaryColumnWidth: 46 };\n\nconst THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning (~1k tokens)\",\n\tlow: \"Light reasoning (~2k tokens)\",\n\tmedium: \"Moderate reasoning (~8k tokens)\",\n\thigh: \"Deep reasoning (~16k tokens)\",\n\txhigh: \"Extra-high reasoning (~32k tokens)\",\n\tmax: \"Maximum reasoning\",\n};\n\nconst DEFAULT_PROJECT_TRUST_LABELS: Record<DefaultProjectTrust, string> = {\n\task: \"Ask\",\n\talways: \"Always trust\",\n\tnever: \"Never trust\",\n};\n\nconst DEFAULT_PROJECT_TRUST_BY_LABEL = new Map(\n\tObject.entries(DEFAULT_PROJECT_TRUST_LABELS).map(([value, label]) => [label, value as DefaultProjectTrust]),\n);\n\nconst PERMISSION_MODE_DESCRIPTIONS: Record<PermissionMode, string> = {\n\tdefault: \"Ask for anything policy does not already allow\",\n\tplan: \"Block file writes, commands, and delegation while you investigate\",\n\tacceptEdits: \"Auto-allow plain file edits; still ask for commands and network\",\n\tdontAsk: \"Never prompt; deny anything not already permitted\",\n\tbypassPermissions: \"Allow every tool call without asking\",\n};\n\n/**\n * What outranks a user-scope write, phrased for someone deciding whether to bother.\n * `user` and `default` are absent because neither shadows anything this menu writes.\n */\nexport const PERMISSION_MODE_OVERRIDE_HINTS: Partial<Record<PermissionModeOrigin, string>> = {\n\tflag: \"--permission-mode on the command line\",\n\tlocal: \".apex-code/permissions.local.json in this project\",\n\tproject: \".apex-code/permissions.json in this project\",\n\tcommand: \"a command-scoped override in this session\",\n\tsession: \"a session-scoped override\",\n};\n\n/** Empty when the session has no permission gate, so the row simply does not exist. */\nfunction permissionModeItems(current: SettingsConfig[\"permissionMode\"], callbacks: SettingsCallbacks): SettingItem[] {\n\tif (!current) return [];\n\treturn [\n\t\t{\n\t\t\tid: \"permission-mode\",\n\t\t\tlabel: \"Permission mode\",\n\t\t\tdescription: PERMISSION_MODE_DESCRIPTIONS[current.mode],\n\t\t\tcurrentValue: current.mode,\n\t\t\tsubmenu: (currentValue, done) =>\n\t\t\t\tnew PermissionModeSubmenu(\n\t\t\t\t\tcurrentValue as PermissionMode,\n\t\t\t\t\tcurrent.origin,\n\t\t\t\t\t(mode) => callbacks.onPermissionModeChange(mode),\n\t\t\t\t\tdone,\n\t\t\t\t),\n\t\t},\n\t];\n}\n\nexport interface SettingsConfig {\n\tautoCompact: boolean;\n\tdefaultModel: string;\n\tcurrentModel?: Model<any>;\n\tavailableDefaultModels: readonly Model<any>[];\n\tshowImages: boolean;\n\timageWidthCells: number;\n\tautoResizeImages: boolean;\n\tblockImages: boolean;\n\tenableSkillCommands: boolean;\n\tsteeringMode: \"all\" | \"one-at-a-time\";\n\tfollowUpMode: \"all\" | \"one-at-a-time\";\n\ttransport: Transport;\n\thttpIdleTimeoutMs: number;\n\tthinkingLevel: ThinkingLevel;\n\tavailableThinkingLevels: ThinkingLevel[];\n\tmodelThinkingLevels: Record<string, ThinkingLevel>;\n\tcurrentTheme: string;\n\tterminalTheme: TerminalTheme;\n\tavailableThemes: string[];\n\thideThinkingBlock: boolean;\n\tmermaidRenderingMode: MermaidRenderingMode;\n\tshowCacheMissNotices: boolean;\n\tcollapseChangelog: boolean;\n\tsendProviderAttribution: boolean;\n\tdoubleEscapeAction: \"fork\" | \"tree\" | \"none\";\n\ttreeFilterMode: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\";\n\tshowHardwareCursor: boolean;\n\teditorPaddingX: number;\n\toutputPad: 0 | 1;\n\tautocompleteMaxVisible: number;\n\tquietStartup: boolean;\n\tdefaultProjectTrust: DefaultProjectTrust;\n\t/** Absent when the session runs without a permission gate; the row is then hidden. */\n\tpermissionMode?: { mode: PermissionMode; origin: PermissionModeOrigin };\n\tclearOnShrink: boolean;\n\tshowTerminalProgress: boolean;\n\ttuiMode: TuiMode;\n\tfullscreenExitOutput: FullscreenExitOutput;\n\tfullscreenScrollbar: ScrollViewScrollbar;\n\tfullscreenCopyOnSelect: boolean;\n\twarnings: WarningSettings;\n}\n\nexport interface SettingsCallbacks {\n\tonAutoCompactChange: (enabled: boolean) => void;\n\tonShowImagesChange: (enabled: boolean) => void;\n\tonImageWidthCellsChange: (width: number) => void;\n\tonAutoResizeImagesChange: (enabled: boolean) => void;\n\tonBlockImagesChange: (blocked: boolean) => void;\n\tonEnableSkillCommandsChange: (enabled: boolean) => void;\n\tonSteeringModeChange: (mode: \"all\" | \"one-at-a-time\") => void;\n\tonFollowUpModeChange: (mode: \"all\" | \"one-at-a-time\") => void;\n\tonTransportChange: (transport: Transport) => void;\n\tonHttpIdleTimeoutMsChange: (timeoutMs: number) => void;\n\tonModelThinkingLevelChange: (provider: string, modelId: string, level: ThinkingLevel) => void;\n\tonModelThinkingLevelRemove: (provider: string, modelId: string) => void;\n\tonThemeChange: (theme: string) => void;\n\tonThemePreview?: (theme: string) => void;\n\tonHideThinkingBlockChange: (hidden: boolean) => void;\n\tonMermaidRenderingModeChange: (mode: MermaidRenderingMode) => void;\n\tonShowCacheMissNoticesChange: (shown: boolean) => void;\n\tonCollapseChangelogChange: (collapsed: boolean) => void;\n\tonSendProviderAttributionChange: (enabled: boolean) => void;\n\tonDoubleEscapeActionChange: (action: \"fork\" | \"tree\" | \"none\") => void;\n\tonTreeFilterModeChange: (mode: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\") => void;\n\tonShowHardwareCursorChange: (enabled: boolean) => void;\n\tonEditorPaddingXChange: (padding: number) => void;\n\tonOutputPadChange: (padding: 0 | 1) => void;\n\tonAutocompleteMaxVisibleChange: (maxVisible: number) => void;\n\tonQuietStartupChange: (enabled: boolean) => void;\n\tonDefaultProjectTrustChange: (defaultProjectTrust: DefaultProjectTrust) => void;\n\tonPermissionModeChange: (mode: PermissionMode) => void;\n\tonClearOnShrinkChange: (enabled: boolean) => void;\n\tonShowTerminalProgressChange: (enabled: boolean) => void;\n\tonTuiModeChange: (mode: TuiMode) => void;\n\tonFullscreenExitOutputChange: (output: FullscreenExitOutput) => void;\n\tonFullscreenScrollbarChange: (mode: ScrollViewScrollbar) => void;\n\tonFullscreenCopyOnSelectChange: (enabled: boolean) => void;\n\tonWarningsChange: (warnings: WarningSettings) => void;\n\tonCancel: () => void;\n}\n\n/**\n * A submenu component for selecting from a list of options.\n */\nclass WarningSettingsSubmenu extends Container {\n\tprivate settingsList: SettingsList;\n\tprivate state: WarningSettings;\n\n\tconstructor(warnings: WarningSettings, onChange: (warnings: WarningSettings) => void, onCancel: () => void) {\n\t\tsuper();\n\n\t\tthis.state = { ...warnings };\n\n\t\tconst items: SettingItem[] = [\n\t\t\t{\n\t\t\t\tid: \"anthropic-extra-usage\",\n\t\t\t\tlabel: \"Anthropic extra usage\",\n\t\t\t\tdescription: \"Warn when Anthropic subscription auth may use paid extra usage\",\n\t\t\t\tcurrentValue: (this.state.anthropicExtraUsage ?? true) ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t];\n\n\t\tthis.settingsList = new SettingsList(\n\t\t\titems,\n\t\t\tMath.min(items.length, 10),\n\t\t\tgetSettingsListTheme(),\n\t\t\t(id, newValue) => {\n\t\t\t\tswitch (id) {\n\t\t\t\t\tcase \"anthropic-extra-usage\":\n\t\t\t\t\t\tthis.state = { ...this.state, anthropicExtraUsage: newValue === \"true\" };\n\t\t\t\t\t\tonChange({ ...this.state });\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\tonCancel,\n\t\t);\n\n\t\tthis.addChild(this.settingsList);\n\t}\n\n\thandleInput(data: string): void {\n\t\tthis.settingsList.handleInput(data);\n\t}\n}\n\nconst CLEAR_OVERRIDE_VALUE = \"__clear__\";\n\nfunction modelSettingKey(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction modelDisplayLabel(model: Model<any>): string {\n\treturn `${model.id} [${model.provider}]`;\n}\n\nfunction modelThinkingOverridesSummary(overrides: Record<string, ThinkingLevel>): string {\n\tconst count = Object.keys(overrides).length;\n\tif (count === 0) return \"none\";\n\treturn `${count} configured`;\n}\n\nfunction modelItemLabel(model: Model<any>): string {\n\treturn `${model.id} ${theme.fg(\"muted\", `[${model.provider}]`)}`;\n}\n\n/**\n * Permission mode picker, mode list then a confirm step for `bypassPermissions`.\n * The confirm exists because the mode list is arrow-cyclable like every other\n * settings row, so \"allow every tool call\" is otherwise one stray keypress away.\n */\nclass PermissionModeSubmenu extends Container {\n\tprivate inputComponent: Component | undefined;\n\tprivate readonly current: PermissionMode;\n\tprivate readonly origin: PermissionModeOrigin;\n\tprivate readonly onSelect: (mode: PermissionMode) => void;\n\tprivate readonly onDone: (selectedValue?: string) => void;\n\n\tconstructor(\n\t\tcurrent: PermissionMode,\n\t\torigin: PermissionModeOrigin,\n\t\tonSelect: (mode: PermissionMode) => void,\n\t\tonDone: (selectedValue?: string) => void,\n\t) {\n\t\tsuper();\n\t\tthis.current = current;\n\t\tthis.origin = origin;\n\t\tthis.onSelect = onSelect;\n\t\tthis.onDone = onDone;\n\t\tthis.showModeList();\n\t}\n\n\thandleInput(data: string): void {\n\t\tthis.inputComponent?.handleInput?.(data);\n\t}\n\n\tprivate setContent(component: Component): void {\n\t\tthis.clear();\n\t\tthis.addChild(component);\n\t\tthis.inputComponent = component;\n\t}\n\n\tprivate description(): string {\n\t\tconst overridden =\n\t\t\tthis.origin !== \"user\" && this.origin !== \"default\"\n\t\t\t\t? ` Currently set by ${PERMISSION_MODE_OVERRIDE_HINTS[this.origin]}.`\n\t\t\t\t: \"\";\n\t\treturn `Saved to user scope and applied to the next tool call.${overridden}`;\n\t}\n\n\tprivate showModeList(): void {\n\t\tthis.setContent(\n\t\t\tnew SelectSubmenu(\n\t\t\t\t\"Permission mode\",\n\t\t\t\tthis.description(),\n\t\t\t\tPERMISSION_MODES.map((mode) => ({\n\t\t\t\t\tvalue: mode,\n\t\t\t\t\tlabel: mode,\n\t\t\t\t\tdescription: PERMISSION_MODE_DESCRIPTIONS[mode],\n\t\t\t\t})),\n\t\t\t\tthis.current,\n\t\t\t\t(value) => {\n\t\t\t\t\tconst mode = value as PermissionMode;\n\t\t\t\t\tif (mode === \"bypassPermissions\") {\n\t\t\t\t\t\tthis.showBypassConfirm();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tthis.onSelect(mode);\n\t\t\t\t\tthis.onDone(mode);\n\t\t\t\t},\n\t\t\t\t() => this.onDone(),\n\t\t\t),\n\t\t);\n\t}\n\n\tprivate showBypassConfirm(): void {\n\t\tthis.setContent(\n\t\t\tnew SelectSubmenu(\n\t\t\t\t\"Bypass all permission checks?\",\n\t\t\t\t\"Every tool call runs without asking, in every project, until you change this back. The OS sandbox still confines writes to the workspace and network egress to the allowlist.\",\n\t\t\t\t[\n\t\t\t\t\t{ value: \"no\", label: `No, keep ${this.current}`, description: \"Return to the mode list\" },\n\t\t\t\t\t{ value: \"yes\", label: \"Yes, bypass\", description: \"Allow every tool call without asking\" },\n\t\t\t\t],\n\t\t\t\t\"no\",\n\t\t\t\t(value) => {\n\t\t\t\t\tif (value !== \"yes\") {\n\t\t\t\t\t\tthis.showModeList();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tthis.onSelect(\"bypassPermissions\");\n\t\t\t\t\tthis.onDone(\"bypassPermissions\");\n\t\t\t\t},\n\t\t\t\t() => this.showModeList(),\n\t\t\t),\n\t\t);\n\t}\n}\n\nfunction themeItems(availableThemes: string[]): SelectItem[] {\n\treturn availableThemes.map((name) => ({ value: name, label: name }));\n}\n\nconst AUTOMATIC_THEME_VALUE = \"/\";\n\nfunction singleModeThemeItems(availableThemes: string[]): SelectItem[] {\n\treturn [\n\t\t{\n\t\t\tvalue: AUTOMATIC_THEME_VALUE,\n\t\t\tlabel: \"Automatic\",\n\t\t\tdescription: \"Use separate themes for light and dark terminal appearance\",\n\t\t},\n\t\t...themeItems(availableThemes),\n\t];\n}\n\nfunction preferredTheme(availableThemes: string[], preferred: string | undefined, fallback: string): string {\n\tif (preferred && availableThemes.includes(preferred)) return preferred;\n\tif (availableThemes.includes(fallback)) return fallback;\n\treturn availableThemes[0] ?? fallback;\n}\n\nfunction defaultAutomaticThemes(\n\tcurrentThemeSetting: string,\n\tavailableThemes: string[],\n): { lightTheme: string; darkTheme: string } {\n\tconst autoTheme = parseAutoThemeSetting(currentThemeSetting);\n\tif (autoTheme) return autoTheme;\n\n\tconst currentFixedTheme = currentThemeSetting.includes(\"/\") ? undefined : currentThemeSetting;\n\tconst themeName = preferredTheme(availableThemes, currentFixedTheme, \"dark\");\n\treturn { lightTheme: themeName, darkTheme: themeName };\n}\n\nclass ThemeSubmenu extends Container {\n\tprivate inputComponent: Component | undefined;\n\tprivate readonly callbacks: SettingsCallbacks;\n\tprivate readonly availableThemes: string[];\n\tprivate readonly terminalTheme: TerminalTheme;\n\tprivate readonly onDone: (selectedValue?: string) => void;\n\tprivate readonly originalThemeSetting: string;\n\tprivate mode: \"single\" | \"automatic\";\n\tprivate singleTheme: string;\n\tprivate lightTheme: string;\n\tprivate darkTheme: string;\n\n\tconstructor(\n\t\tcurrentThemeSetting: string,\n\t\tterminalTheme: TerminalTheme,\n\t\tavailableThemes: string[],\n\t\tcallbacks: SettingsCallbacks,\n\t\tonDone: (selectedValue?: string) => void,\n\t) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\t\tthis.availableThemes = availableThemes;\n\t\tthis.terminalTheme = terminalTheme;\n\t\tthis.onDone = onDone;\n\t\tthis.originalThemeSetting = currentThemeSetting;\n\t\tconst autoTheme = parseAutoThemeSetting(currentThemeSetting);\n\t\tconst automaticThemes = defaultAutomaticThemes(currentThemeSetting, availableThemes);\n\t\tconst fixedTheme = autoTheme || currentThemeSetting.includes(\"/\") ? undefined : currentThemeSetting;\n\t\tthis.mode = autoTheme ? \"automatic\" : \"single\";\n\t\tthis.lightTheme = automaticThemes.lightTheme;\n\t\tthis.darkTheme = automaticThemes.darkTheme;\n\t\tthis.singleTheme = preferredTheme(\n\t\t\tavailableThemes,\n\t\t\tfixedTheme ?? (autoTheme ? this.getActiveAutomaticTheme() : undefined),\n\t\t\t\"dark\",\n\t\t);\n\n\t\tif (this.mode === \"automatic\") {\n\t\t\tthis.showAutomaticMenu();\n\t\t} else {\n\t\t\tthis.showSingleMenu();\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tthis.inputComponent?.handleInput?.(data);\n\t}\n\n\tprivate setContent(renderComponent: Component, inputComponent: Component = renderComponent): void {\n\t\tthis.clear();\n\t\tthis.addChild(renderComponent);\n\t\tthis.inputComponent = inputComponent;\n\t}\n\n\tprivate showSingleMenu(): void {\n\t\tthis.mode = \"single\";\n\t\tconst menu = new SelectSubmenu(\n\t\t\t\"Theme\",\n\t\t\t\"Select a theme, or choose Automatic to follow terminal appearance.\",\n\t\t\tsingleModeThemeItems(this.availableThemes),\n\t\t\tthis.singleTheme,\n\t\t\t(value) => {\n\t\t\t\tif (value === AUTOMATIC_THEME_VALUE) {\n\t\t\t\t\tthis.mode = \"automatic\";\n\t\t\t\t\tthis.callbacks.onThemePreview?.(this.getThemeSetting());\n\t\t\t\t\tthis.showAutomaticMenu();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.singleTheme = value;\n\t\t\t\tthis.apply(value);\n\t\t\t},\n\t\t\t() => this.cancel(),\n\t\t\t(value) => {\n\t\t\t\tthis.callbacks.onThemePreview?.(value === AUTOMATIC_THEME_VALUE ? this.getAutomaticThemeSetting() : value);\n\t\t\t},\n\t\t);\n\t\tthis.setContent(menu);\n\t}\n\n\tprivate showAutomaticMenu(): void {\n\t\tthis.mode = \"automatic\";\n\t\tconst content = new Container();\n\t\tcontent.addChild(new Text(theme.bold(theme.fg(\"accent\", \"Automatic Theme\")), 0, 0));\n\t\tcontent.addChild(new Spacer(1));\n\t\tcontent.addChild(new Text(theme.fg(\"muted\", \"Choose themes for terminal light and dark appearance.\"), 0, 0));\n\t\tcontent.addChild(new Text(theme.fg(\"muted\", \"Light/dark detection requires terminal support.\"), 0, 0));\n\t\tcontent.addChild(new Spacer(1));\n\n\t\tconst items: SettingItem[] = [\n\t\t\t{\n\t\t\t\tid: \"light-theme\",\n\t\t\t\tlabel: \"Light theme\",\n\t\t\t\tdescription: \"Theme to use in automatic mode when the terminal is light\",\n\t\t\t\tcurrentValue: this.lightTheme,\n\t\t\t\tsubmenu: (currentValue, done) =>\n\t\t\t\t\tthis.createThemeSelect(\n\t\t\t\t\t\t\"Light Theme\",\n\t\t\t\t\t\t\"Select the theme to use for light terminal appearance\",\n\t\t\t\t\t\tcurrentValue,\n\t\t\t\t\t\tdone,\n\t\t\t\t\t\t(value) => {\n\t\t\t\t\t\t\tthis.lightTheme = value;\n\t\t\t\t\t\t\tthis.callbacks.onThemePreview?.(this.getThemeSetting());\n\t\t\t\t\t\t\tdone(value);\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"dark-theme\",\n\t\t\t\tlabel: \"Dark theme\",\n\t\t\t\tdescription: \"Theme to use in automatic mode when the terminal is dark\",\n\t\t\t\tcurrentValue: this.darkTheme,\n\t\t\t\tsubmenu: (currentValue, done) =>\n\t\t\t\t\tthis.createThemeSelect(\n\t\t\t\t\t\t\"Dark Theme\",\n\t\t\t\t\t\t\"Select the theme to use for dark terminal appearance\",\n\t\t\t\t\t\tcurrentValue,\n\t\t\t\t\t\tdone,\n\t\t\t\t\t\t(value) => {\n\t\t\t\t\t\t\tthis.darkTheme = value;\n\t\t\t\t\t\t\tthis.callbacks.onThemePreview?.(this.getThemeSetting());\n\t\t\t\t\t\t\tdone(value);\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"apply\",\n\t\t\t\tlabel: \"Apply\",\n\t\t\t\tdescription: \"Save and go back\",\n\t\t\t\tcurrentValue: \"save and go back\",\n\t\t\t\tvalues: [\"save and go back\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"single-mode\",\n\t\t\t\tlabel: \"Change mode\",\n\t\t\t\tdescription: \"Switch to one theme for light and dark\",\n\t\t\t\tcurrentValue: \"switch to single theme\",\n\t\t\t\tvalues: [\"switch to single theme\"],\n\t\t\t},\n\t\t];\n\n\t\tconst settingsList = new SettingsList(\n\t\t\titems,\n\t\t\tMath.min(items.length, 10),\n\t\t\tgetSettingsListTheme(),\n\t\t\t(id) => {\n\t\t\t\tswitch (id) {\n\t\t\t\t\tcase \"single-mode\":\n\t\t\t\t\t\tthis.mode = \"single\";\n\t\t\t\t\t\tthis.singleTheme = this.getActiveAutomaticTheme();\n\t\t\t\t\t\tthis.callbacks.onThemePreview?.(this.singleTheme);\n\t\t\t\t\t\tthis.showSingleMenu();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"apply\":\n\t\t\t\t\t\tthis.apply(this.getAutomaticThemeSetting());\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => this.cancel(),\n\t\t);\n\t\tcontent.addChild(settingsList);\n\t\tthis.setContent(content, settingsList);\n\t}\n\n\tprivate createThemeSelect(\n\t\ttitle: string,\n\t\tdescription: string,\n\t\tcurrentValue: string,\n\t\tdone: (selectedValue?: string) => void,\n\t\tonSelect: (value: string) => void,\n\t): SelectSubmenu {\n\t\treturn new SelectSubmenu(\n\t\t\ttitle,\n\t\t\tdescription,\n\t\t\tthemeItems(this.availableThemes),\n\t\t\tcurrentValue,\n\t\t\tonSelect,\n\t\t\t() => {\n\t\t\t\tthis.callbacks.onThemePreview?.(this.getThemeSetting());\n\t\t\t\tdone();\n\t\t\t},\n\t\t\t(value) => this.callbacks.onThemePreview?.(value),\n\t\t);\n\t}\n\n\tprivate getThemeSetting(): string {\n\t\treturn this.mode === \"automatic\" ? this.getAutomaticThemeSetting() : this.singleTheme;\n\t}\n\n\tprivate getActiveAutomaticTheme(): string {\n\t\treturn this.terminalTheme === \"light\" ? this.lightTheme : this.darkTheme;\n\t}\n\n\tprivate getAutomaticThemeSetting(): string {\n\t\treturn `${this.lightTheme}/${this.darkTheme}`;\n\t}\n\n\tprivate apply(themeSetting: string): void {\n\t\tthis.onDone(themeSetting);\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.callbacks.onThemePreview?.(this.originalThemeSetting);\n\t\tthis.onDone();\n\t}\n}\n\n/**\n * Main settings selector component.\n */\nexport class SettingsSelectorComponent extends Container {\n\tprivate settingsList: SettingsList;\n\n\tconstructor(config: SettingsConfig, callbacks: SettingsCallbacks) {\n\t\tsuper();\n\n\t\tconst supportsImages = getCapabilities().images;\n\t\tconst followUpKey = keyDisplayText(\"app.message.followUp\");\n\t\tconst cycleThinkingKey = keyDisplayText(\"app.thinking.cycle\");\n\t\tlet currentWarnings = { ...config.warnings };\n\t\tconst currentModelThinkingLevels = { ...config.modelThinkingLevels };\n\t\tconst defaultModelByValue = new Map(\n\t\t\tconfig.availableDefaultModels.map((model) => [modelSettingKey(model), model]),\n\t\t);\n\t\tconst currentDefaultModelKey = defaultModelByValue.has(config.defaultModel) ? config.defaultModel : undefined;\n\t\tconst currentModelKey = config.currentModel ? modelSettingKey(config.currentModel) : undefined;\n\n\t\tconst items: SettingItem[] = [\n\t\t\t{\n\t\t\t\tid: \"autocompact\",\n\t\t\t\tlabel: \"Auto-compact\",\n\t\t\t\tdescription: \"Automatically compact context when it gets too large\",\n\t\t\t\tcurrentValue: config.autoCompact ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"steering-mode\",\n\t\t\t\tlabel: \"Steering mode\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Enter while streaming queues steering messages. 'one-at-a-time': deliver one, wait for response. 'all': deliver all at once.\",\n\t\t\t\tcurrentValue: config.steeringMode,\n\t\t\t\tvalues: [\"one-at-a-time\", \"all\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"follow-up-mode\",\n\t\t\t\tlabel: \"Follow-up mode\",\n\t\t\t\tdescription: `${followUpKey} queues follow-up messages until agent stops. 'one-at-a-time': deliver one, wait for response. 'all': deliver all at once.`,\n\t\t\t\tcurrentValue: config.followUpMode,\n\t\t\t\tvalues: [\"one-at-a-time\", \"all\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"transport\",\n\t\t\t\tlabel: \"Transport\",\n\t\t\t\tdescription: \"Preferred transport for providers that support multiple transports\",\n\t\t\t\tcurrentValue: config.transport,\n\t\t\t\tvalues: [\"sse\", \"websocket\", \"websocket-cached\", \"auto\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"http-idle-timeout\",\n\t\t\t\tlabel: \"HTTP idle timeout\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Maximum idle gap while waiting for HTTP headers or body chunks. Disable for local models that pause longer than five minutes.\",\n\t\t\t\tcurrentValue: formatHttpIdleTimeoutMs(config.httpIdleTimeoutMs),\n\t\t\t\tvalues: HTTP_IDLE_TIMEOUT_CHOICES.map((choice) => choice.label),\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"hide-thinking\",\n\t\t\t\tlabel: \"Hide thinking\",\n\t\t\t\tdescription: \"Hide thinking blocks in assistant responses\",\n\t\t\t\tcurrentValue: config.hideThinkingBlock ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"mermaid-rendering\",\n\t\t\t\tlabel: \"Mermaid diagrams\",\n\t\t\t\tdescription: \"Render Mermaid code blocks as Unicode diagrams\",\n\t\t\t\tcurrentValue: config.mermaidRenderingMode,\n\t\t\t\tvalues: [\"off\", \"final\", \"streaming\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"cache-miss-notices\",\n\t\t\t\tlabel: \"Cache miss notices\",\n\t\t\t\tdescription: \"Show transcript notices for significant prompt-cache misses and compaction costs\",\n\t\t\t\tcurrentValue: config.showCacheMissNotices ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"collapse-changelog\",\n\t\t\t\tlabel: \"Collapse changelog\",\n\t\t\t\tdescription: \"Show condensed changelog after updates\",\n\t\t\t\tcurrentValue: config.collapseChangelog ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"quiet-startup\",\n\t\t\t\tlabel: \"Quiet startup\",\n\t\t\t\tdescription: \"Disable verbose printing at startup\",\n\t\t\t\tcurrentValue: config.quietStartup ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"send-provider-attribution\",\n\t\t\t\tlabel: \"Provider attribution headers\",\n\t\t\t\tdescription: \"Identify this CLI to your configured LLM provider (e.g. OpenRouter billing origin)\",\n\t\t\t\tcurrentValue: config.sendProviderAttribution ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"default-project-trust\",\n\t\t\t\tlabel: \"Default project trust\",\n\t\t\t\tdescription: \"Fallback behavior when no extension or saved trust decision decides project trust\",\n\t\t\t\tcurrentValue: DEFAULT_PROJECT_TRUST_LABELS[config.defaultProjectTrust],\n\t\t\t\tvalues: Object.values(DEFAULT_PROJECT_TRUST_LABELS),\n\t\t\t},\n\t\t\t...permissionModeItems(config.permissionMode, callbacks),\n\t\t\t{\n\t\t\t\tid: \"double-escape-action\",\n\t\t\t\tlabel: \"Double-escape action\",\n\t\t\t\tdescription: \"Action when pressing Escape twice with empty editor\",\n\t\t\t\tcurrentValue: config.doubleEscapeAction,\n\t\t\t\tvalues: [\"tree\", \"fork\", \"none\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"tree-filter-mode\",\n\t\t\t\tlabel: \"Tree filter mode\",\n\t\t\t\tdescription: \"Default filter when opening /tree\",\n\t\t\t\tcurrentValue: config.treeFilterMode,\n\t\t\t\tvalues: [\"default\", \"no-tools\", \"user-only\", \"labeled-only\", \"all\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"warnings\",\n\t\t\t\tlabel: \"Warnings\",\n\t\t\t\tdescription: \"Enable or disable individual warnings\",\n\t\t\t\tcurrentValue: \"configure\",\n\t\t\t\tsubmenu: (_currentValue, done) =>\n\t\t\t\t\tnew WarningSettingsSubmenu(\n\t\t\t\t\t\tcurrentWarnings,\n\t\t\t\t\t\t(warnings) => {\n\t\t\t\t\t\t\tcurrentWarnings = warnings;\n\t\t\t\t\t\t\tcallbacks.onWarningsChange(warnings);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t() => done(),\n\t\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"model-thinking\",\n\t\t\t\tlabel: \"Default thinking level per model\",\n\t\t\t\tdescription: `Override the default thinking level for specific models. ${cycleThinkingKey} cycles in-session.`,\n\t\t\t\tcurrentValue: modelThinkingOverridesSummary(currentModelThinkingLevels),\n\t\t\t\tsubmenu: (_currentValue, done) => {\n\t\t\t\t\tconst steps: SteppedSubmenuStep[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkey: \"model\",\n\t\t\t\t\t\t\ttitle: \"Per-Model Thinking Level\",\n\t\t\t\t\t\t\tdescription: \"Select a model to configure\",\n\t\t\t\t\t\t\toptions: () => {\n\t\t\t\t\t\t\t\tconst sorted = [...config.availableDefaultModels].sort((a, b) => {\n\t\t\t\t\t\t\t\t\tconst aKey = modelSettingKey(a);\n\t\t\t\t\t\t\t\t\tconst bKey = modelSettingKey(b);\n\t\t\t\t\t\t\t\t\tif (aKey === currentModelKey) return -1;\n\t\t\t\t\t\t\t\t\tif (bKey === currentModelKey) return 1;\n\t\t\t\t\t\t\t\t\tif (aKey === currentDefaultModelKey) return -1;\n\t\t\t\t\t\t\t\t\tif (bKey === currentDefaultModelKey) return 1;\n\t\t\t\t\t\t\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tconst items: SelectItem[] = sorted.map((model) => {\n\t\t\t\t\t\t\t\t\tconst key = modelSettingKey(model);\n\t\t\t\t\t\t\t\t\tconst override = currentModelThinkingLevels[key];\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\tvalue: key,\n\t\t\t\t\t\t\t\t\t\tlabel: modelItemLabel(model),\n\t\t\t\t\t\t\t\t\t\tdescription: override ?? undefined,\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tif (items.length === 0) {\n\t\t\t\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\t\t\t\tvalue: \"__none__\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"No models available\",\n\t\t\t\t\t\t\t\t\t\tdescription: \"Log in to a provider or configure an API key first\",\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn items;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpreselect: () => currentModelKey ?? currentDefaultModelKey,\n\t\t\t\t\t\t\tsearchable: true,\n\t\t\t\t\t\t\tlayout: MODEL_PICKER_LAYOUT,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkey: \"level\",\n\t\t\t\t\t\t\ttitle: (ctx) => {\n\t\t\t\t\t\t\t\tconst m = defaultModelByValue.get(ctx.model);\n\t\t\t\t\t\t\t\treturn `Thinking Level for ${m ? modelDisplayLabel(m) : ctx.model}`;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tdescription: \"Select default thinking level for this model\",\n\t\t\t\t\t\t\toptions: (ctx) => {\n\t\t\t\t\t\t\t\tconst model = defaultModelByValue.get(ctx.model);\n\t\t\t\t\t\t\t\tif (!model) return [];\n\t\t\t\t\t\t\t\tconst levels = (\n\t\t\t\t\t\t\t\t\tmodel.reasoning ? getSupportedThinkingLevels(model) : [\"off\"]\n\t\t\t\t\t\t\t\t) as ThinkingLevel[];\n\t\t\t\t\t\t\t\tconst items: SelectItem[] = levels.map((level) => ({\n\t\t\t\t\t\t\t\t\tvalue: level,\n\t\t\t\t\t\t\t\t\tlabel: level,\n\t\t\t\t\t\t\t\t\tdescription: THINKING_DESCRIPTIONS[level],\n\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\tif (currentModelThinkingLevels[ctx.model] !== undefined) {\n\t\t\t\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\t\t\t\tvalue: CLEAR_OVERRIDE_VALUE,\n\t\t\t\t\t\t\t\t\t\tlabel: \"(clear override)\",\n\t\t\t\t\t\t\t\t\t\tdescription: `Revert to global default (${config.thinkingLevel})`,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn items;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpreselect: (ctx) => currentModelThinkingLevels[ctx.model],\n\t\t\t\t\t\t},\n\t\t\t\t\t];\n\n\t\t\t\t\tconst summary = () => modelThinkingOverridesSummary(currentModelThinkingLevels);\n\n\t\t\t\t\treturn new SteppedSubmenu(\n\t\t\t\t\t\tsteps,\n\t\t\t\t\t\t(selections) => {\n\t\t\t\t\t\t\tconst model = defaultModelByValue.get(selections.model);\n\t\t\t\t\t\t\tif (!model) return;\n\t\t\t\t\t\t\tif (selections.level === CLEAR_OVERRIDE_VALUE) {\n\t\t\t\t\t\t\t\tcallbacks.onModelThinkingLevelRemove(model.provider, model.id);\n\t\t\t\t\t\t\t\tdelete currentModelThinkingLevels[selections.model];\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcallbacks.onModelThinkingLevelChange(\n\t\t\t\t\t\t\t\t\tmodel.provider,\n\t\t\t\t\t\t\t\t\tmodel.id,\n\t\t\t\t\t\t\t\t\tselections.level as ThinkingLevel,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tcurrentModelThinkingLevels[selections.model] = selections.level as ThinkingLevel;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\tdone(summary());\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ loop: true },\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"tui-mode\",\n\t\t\t\tlabel: \"TUI mode\",\n\t\t\t\tdescription: \"Interface layout; fullscreen mode is experimental\",\n\t\t\t\tcurrentValue: config.tuiMode,\n\t\t\t\tvalues: [\"regular\", \"fullscreen\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"fullscreen-exit-output\",\n\t\t\t\tlabel: \"Fullscreen exit output\",\n\t\t\t\tdescription: \"Print the transcript or only a session resume hint when exiting fullscreen mode\",\n\t\t\t\tcurrentValue: config.fullscreenExitOutput,\n\t\t\t\tvalues: [\"transcript\", \"resume-hint\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"fullscreen-scrollbar\",\n\t\t\t\tlabel: \"Fullscreen scrollbar\",\n\t\t\t\tdescription: \"Scrollbar behavior in fullscreen mode; has no effect in regular mode\",\n\t\t\t\tcurrentValue: config.fullscreenScrollbar,\n\t\t\t\tvalues: [\"auto\", \"always\", \"hidden\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"fullscreen-copy-on-select\",\n\t\t\t\tlabel: \"Fullscreen copy on select\",\n\t\t\t\tdescription: \"Automatically copy selected text in fullscreen mode; disable to copy selections with Ctrl+X\",\n\t\t\t\tcurrentValue: config.fullscreenCopyOnSelect ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"theme\",\n\t\t\t\tlabel: \"Theme\",\n\t\t\t\tdescription: \"Color theme for the interface\",\n\t\t\t\tcurrentValue: config.currentTheme,\n\t\t\t\tsubmenu: (currentValue, done) =>\n\t\t\t\t\tnew ThemeSubmenu(currentValue, config.terminalTheme, config.availableThemes, callbacks, done),\n\t\t\t},\n\t\t];\n\n\t\t// Only show image toggle if terminal supports it\n\t\tif (supportsImages) {\n\t\t\t// Insert after autocompact\n\t\t\titems.splice(1, 0, {\n\t\t\t\tid: \"show-images\",\n\t\t\t\tlabel: \"Show images\",\n\t\t\t\tdescription: \"Render images inline in terminal\",\n\t\t\t\tcurrentValue: config.showImages ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t});\n\t\t\titems.splice(2, 0, {\n\t\t\t\tid: \"image-width-cells\",\n\t\t\t\tlabel: \"Image width\",\n\t\t\t\tdescription: \"Preferred inline image width in terminal cells\",\n\t\t\t\tcurrentValue: String(config.imageWidthCells),\n\t\t\t\tvalues: [\"60\", \"80\", \"120\"],\n\t\t\t});\n\t\t}\n\n\t\t// Image auto-resize toggle (always available, affects both attached and read images)\n\t\titems.splice(supportsImages ? 3 : 1, 0, {\n\t\t\tid: \"auto-resize-images\",\n\t\t\tlabel: \"Auto-resize images\",\n\t\t\tdescription: \"Resize large images to 2000x2000 max for better model compatibility\",\n\t\t\tcurrentValue: config.autoResizeImages ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Block images toggle (always available, insert after auto-resize-images)\n\t\tconst autoResizeIndex = items.findIndex((item) => item.id === \"auto-resize-images\");\n\t\titems.splice(autoResizeIndex + 1, 0, {\n\t\t\tid: \"block-images\",\n\t\t\tlabel: \"Block images\",\n\t\t\tdescription: \"Prevent images from being sent to LLM providers\",\n\t\t\tcurrentValue: config.blockImages ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Skill commands toggle (insert after block-images)\n\t\tconst blockImagesIndex = items.findIndex((item) => item.id === \"block-images\");\n\t\titems.splice(blockImagesIndex + 1, 0, {\n\t\t\tid: \"skill-commands\",\n\t\t\tlabel: \"Skill commands\",\n\t\t\tdescription: \"Register skills as /skill:name commands\",\n\t\t\tcurrentValue: config.enableSkillCommands ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Hardware cursor toggle (insert after skill-commands)\n\t\tconst skillCommandsIndex = items.findIndex((item) => item.id === \"skill-commands\");\n\t\titems.splice(skillCommandsIndex + 1, 0, {\n\t\t\tid: \"show-hardware-cursor\",\n\t\t\tlabel: \"Show hardware cursor\",\n\t\t\tdescription: \"Show the terminal cursor while still positioning it for IME support\",\n\t\t\tcurrentValue: config.showHardwareCursor ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Editor padding toggle (insert after show-hardware-cursor)\n\t\tconst hardwareCursorIndex = items.findIndex((item) => item.id === \"show-hardware-cursor\");\n\t\titems.splice(hardwareCursorIndex + 1, 0, {\n\t\t\tid: \"editor-padding\",\n\t\t\tlabel: \"Editor padding\",\n\t\t\tdescription: \"Horizontal padding for input editor (0-3)\",\n\t\t\tcurrentValue: String(config.editorPaddingX),\n\t\t\tvalues: [\"0\", \"1\", \"2\", \"3\"],\n\t\t});\n\n\t\t// Output padding toggle (insert after editor-padding)\n\t\tconst editorPaddingIndex = items.findIndex((item) => item.id === \"editor-padding\");\n\t\titems.splice(editorPaddingIndex + 1, 0, {\n\t\t\tid: \"output-padding\",\n\t\t\tlabel: \"Output padding\",\n\t\t\tdescription: \"Horizontal padding for user messages, assistant messages, and thinking\",\n\t\t\tcurrentValue: String(config.outputPad),\n\t\t\tvalues: [\"0\", \"1\"],\n\t\t});\n\n\t\t// Autocomplete max visible toggle (insert after output-padding)\n\t\tconst outputPaddingIndex = items.findIndex((item) => item.id === \"output-padding\");\n\t\titems.splice(outputPaddingIndex + 1, 0, {\n\t\t\tid: \"autocomplete-max-visible\",\n\t\t\tlabel: \"Autocomplete max items\",\n\t\t\tdescription: \"Max visible items in autocomplete dropdown (3-20)\",\n\t\t\tcurrentValue: String(config.autocompleteMaxVisible),\n\t\t\tvalues: [\"3\", \"5\", \"7\", \"10\", \"15\", \"20\"],\n\t\t});\n\n\t\t// Clear on shrink toggle (insert after autocomplete-max-visible)\n\t\tconst autocompleteIndex = items.findIndex((item) => item.id === \"autocomplete-max-visible\");\n\t\titems.splice(autocompleteIndex + 1, 0, {\n\t\t\tid: \"clear-on-shrink\",\n\t\t\tlabel: \"Clear on shrink\",\n\t\t\tdescription: \"Clear empty rows when content shrinks (may cause flicker)\",\n\t\t\tcurrentValue: config.clearOnShrink ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Terminal progress toggle (insert after clear-on-shrink)\n\t\tconst clearOnShrinkIndex = items.findIndex((item) => item.id === \"clear-on-shrink\");\n\t\titems.splice(clearOnShrinkIndex + 1, 0, {\n\t\t\tid: \"terminal-progress\",\n\t\t\tlabel: \"Terminal progress\",\n\t\t\tdescription: \"Show OSC 9;4 progress indicators in the terminal tab bar\",\n\t\t\tcurrentValue: config.showTerminalProgress ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Add borders\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.settingsList = new SettingsList(\n\t\t\titems,\n\t\t\t10,\n\t\t\tgetSettingsListTheme(),\n\t\t\t(id, newValue) => {\n\t\t\t\tswitch (id) {\n\t\t\t\t\tcase \"autocompact\":\n\t\t\t\t\t\tcallbacks.onAutoCompactChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"show-images\":\n\t\t\t\t\t\tcallbacks.onShowImagesChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"image-width-cells\":\n\t\t\t\t\t\tcallbacks.onImageWidthCellsChange(parseInt(newValue, 10));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"auto-resize-images\":\n\t\t\t\t\t\tcallbacks.onAutoResizeImagesChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"block-images\":\n\t\t\t\t\t\tcallbacks.onBlockImagesChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"skill-commands\":\n\t\t\t\t\t\tcallbacks.onEnableSkillCommandsChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"steering-mode\":\n\t\t\t\t\t\tcallbacks.onSteeringModeChange(newValue as \"all\" | \"one-at-a-time\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"follow-up-mode\":\n\t\t\t\t\t\tcallbacks.onFollowUpModeChange(newValue as \"all\" | \"one-at-a-time\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"transport\":\n\t\t\t\t\t\tcallbacks.onTransportChange(newValue as Transport);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"http-idle-timeout\": {\n\t\t\t\t\t\tconst choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.label === newValue);\n\t\t\t\t\t\tif (choice) {\n\t\t\t\t\t\t\tcallbacks.onHttpIdleTimeoutMsChange(choice.timeoutMs);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"hide-thinking\":\n\t\t\t\t\t\tcallbacks.onHideThinkingBlockChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"mermaid-rendering\":\n\t\t\t\t\t\tcallbacks.onMermaidRenderingModeChange(newValue as MermaidRenderingMode);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"cache-miss-notices\":\n\t\t\t\t\t\tcallbacks.onShowCacheMissNoticesChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"collapse-changelog\":\n\t\t\t\t\t\tcallbacks.onCollapseChangelogChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"quiet-startup\":\n\t\t\t\t\t\tcallbacks.onQuietStartupChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"send-provider-attribution\":\n\t\t\t\t\t\tcallbacks.onSendProviderAttributionChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"default-project-trust\": {\n\t\t\t\t\t\tconst defaultProjectTrust = DEFAULT_PROJECT_TRUST_BY_LABEL.get(newValue);\n\t\t\t\t\t\tif (defaultProjectTrust) {\n\t\t\t\t\t\t\tcallbacks.onDefaultProjectTrustChange(defaultProjectTrust);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"double-escape-action\":\n\t\t\t\t\t\tcallbacks.onDoubleEscapeActionChange(newValue as \"fork\" | \"tree\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"tree-filter-mode\":\n\t\t\t\t\t\tcallbacks.onTreeFilterModeChange(\n\t\t\t\t\t\t\tnewValue as \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"show-hardware-cursor\":\n\t\t\t\t\t\tcallbacks.onShowHardwareCursorChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"editor-padding\":\n\t\t\t\t\t\tcallbacks.onEditorPaddingXChange(parseInt(newValue, 10));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"output-padding\":\n\t\t\t\t\t\tcallbacks.onOutputPadChange(newValue === \"0\" ? 0 : 1);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"autocomplete-max-visible\":\n\t\t\t\t\t\tcallbacks.onAutocompleteMaxVisibleChange(parseInt(newValue, 10));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"clear-on-shrink\":\n\t\t\t\t\t\tcallbacks.onClearOnShrinkChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"terminal-progress\":\n\t\t\t\t\t\tcallbacks.onShowTerminalProgressChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"tui-mode\":\n\t\t\t\t\t\tcallbacks.onTuiModeChange(newValue as TuiMode);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"fullscreen-exit-output\":\n\t\t\t\t\t\tcallbacks.onFullscreenExitOutputChange(newValue as FullscreenExitOutput);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"fullscreen-scrollbar\":\n\t\t\t\t\t\tcallbacks.onFullscreenScrollbarChange(newValue as ScrollViewScrollbar);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"fullscreen-copy-on-select\":\n\t\t\t\t\t\tcallbacks.onFullscreenCopyOnSelectChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"theme\":\n\t\t\t\t\t\tcallbacks.onThemeChange(newValue);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\tcallbacks.onCancel,\n\t\t\t{ enableSearch: true },\n\t\t);\n\n\t\tthis.addChild(this.settingsList);\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tgetSettingsList(): SettingsList {\n\t\treturn this.settingsList;\n\t}\n}\n"]}
1
+ {"version":3,"file":"settings-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/settings-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { getSupportedThinkingLevels, type Model, type Transport } from \"@earendil-works/pi-ai\";\nimport {\n\ttype Component,\n\tContainer,\n\tgetCapabilities,\n\ttype ScrollViewScrollbar,\n\ttype SelectItem,\n\ttype SettingItem,\n\tSettingsList,\n\tSpacer,\n\tText,\n} from \"@earendil-works/pi-tui\";\nimport type { ThinkingLevel } from \"apex-code-agent-core\";\nimport { formatHttpIdleTimeoutMs, HTTP_IDLE_TIMEOUT_CHOICES } from \"../../../core/http-dispatcher.ts\";\nimport type { PermissionModeOrigin } from \"../../../core/permissions/startup.ts\";\nimport { PERMISSION_MODES, type PermissionMode } from \"../../../core/permissions/store.ts\";\nimport type {\n\tDefaultProjectTrust,\n\tFullscreenExitOutput,\n\tMermaidRenderingMode,\n\tTuiMode,\n\tWarningSettings,\n} from \"../../../core/settings-manager.ts\";\nimport { getSettingsListTheme, parseAutoThemeSetting, type TerminalTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyDisplayText } from \"./keybinding-hints.ts\";\nimport { SelectSubmenu, SteppedSubmenu, type SteppedSubmenuStep } from \"./settings-submenu.ts\";\n\nconst MODEL_PICKER_LAYOUT = { minPrimaryColumnWidth: 12, maxPrimaryColumnWidth: 46 };\n\nconst THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning (~1k tokens)\",\n\tlow: \"Light reasoning (~2k tokens)\",\n\tmedium: \"Moderate reasoning (~8k tokens)\",\n\thigh: \"Deep reasoning (~16k tokens)\",\n\txhigh: \"Extra-high reasoning (~32k tokens)\",\n\tmax: \"Maximum reasoning\",\n};\n\nconst DEFAULT_PROJECT_TRUST_LABELS: Record<DefaultProjectTrust, string> = {\n\task: \"Ask\",\n\talways: \"Always trust\",\n\tnever: \"Never trust\",\n};\n\nconst DEFAULT_PROJECT_TRUST_BY_LABEL = new Map(\n\tObject.entries(DEFAULT_PROJECT_TRUST_LABELS).map(([value, label]) => [label, value as DefaultProjectTrust]),\n);\n\nconst PERMISSION_MODE_DESCRIPTIONS: Record<PermissionMode, string> = {\n\tdefault: \"Ask for anything policy does not already allow\",\n\tplan: \"Block file writes, commands, and delegation while you investigate\",\n\tacceptEdits: \"Auto-allow plain file edits; still ask for commands and network\",\n\tdontAsk: \"Never prompt; deny anything not already permitted\",\n\tbypassPermissions: \"Allow every tool call without asking\",\n};\n\n/**\n * What outranks a user-scope write, phrased for someone deciding whether to bother.\n * `user` and `default` are absent because neither shadows anything this menu writes.\n */\nexport const PERMISSION_MODE_OVERRIDE_HINTS: Partial<Record<PermissionModeOrigin, string>> = {\n\tflag: \"--permission-mode on the command line\",\n\tlocal: \".apex-code/permissions.local.json in this project\",\n\tproject: \".apex-code/permissions.json in this project\",\n\tcommand: \"a command-scoped override in this session\",\n\tsession: \"a session-scoped override\",\n};\n\n/** Empty when the session has no permission gate, so the row simply does not exist. */\nfunction permissionModeItems(current: SettingsConfig[\"permissionMode\"], callbacks: SettingsCallbacks): SettingItem[] {\n\tif (!current) return [];\n\treturn [\n\t\t{\n\t\t\tid: \"permission-mode\",\n\t\t\tlabel: \"Permission mode\",\n\t\t\tdescription: PERMISSION_MODE_DESCRIPTIONS[current.mode],\n\t\t\tcurrentValue: current.mode,\n\t\t\tsubmenu: (currentValue, done) =>\n\t\t\t\tnew PermissionModeSubmenu(\n\t\t\t\t\tcurrentValue as PermissionMode,\n\t\t\t\t\tcurrent.origin,\n\t\t\t\t\t(mode) => callbacks.onPermissionModeChange(mode),\n\t\t\t\t\tdone,\n\t\t\t\t),\n\t\t},\n\t];\n}\n\nexport interface SettingsConfig {\n\tautoCompact: boolean;\n\tdefaultModel: string;\n\tcurrentModel?: Model<any>;\n\tavailableDefaultModels: readonly Model<any>[];\n\tshowImages: boolean;\n\timageWidthCells: number;\n\tautoResizeImages: boolean;\n\tblockImages: boolean;\n\tenableSkillCommands: boolean;\n\tsteeringMode: \"all\" | \"one-at-a-time\";\n\tfollowUpMode: \"all\" | \"one-at-a-time\";\n\ttransport: Transport;\n\thttpIdleTimeoutMs: number;\n\tthinkingLevel: ThinkingLevel;\n\tavailableThinkingLevels: ThinkingLevel[];\n\tmodelThinkingLevels: Record<string, ThinkingLevel>;\n\tcurrentTheme: string;\n\tterminalTheme: TerminalTheme;\n\tavailableThemes: string[];\n\thideThinkingBlock: boolean;\n\tmermaidRenderingMode: MermaidRenderingMode;\n\tshowCacheMissNotices: boolean;\n\tcollapseChangelog: boolean;\n\tsendProviderAttribution: boolean;\n\tdoubleEscapeAction: \"fork\" | \"tree\" | \"none\";\n\ttreeFilterMode: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\";\n\tshowHardwareCursor: boolean;\n\teditorPaddingX: number;\n\toutputPad: 0 | 1;\n\tautocompleteMaxVisible: number;\n\tquietStartup: boolean;\n\tdefaultProjectTrust: DefaultProjectTrust;\n\t/** Absent when the session runs without a permission gate; the row is then hidden. */\n\tpermissionMode?: { mode: PermissionMode; origin: PermissionModeOrigin };\n\tclearOnShrink: boolean;\n\tshowTerminalProgress: boolean;\n\ttuiMode: TuiMode;\n\tfullscreenExitOutput: FullscreenExitOutput;\n\tfullscreenScrollbar: ScrollViewScrollbar;\n\tfullscreenCopyOnSelect: boolean;\n\twarnings: WarningSettings;\n}\n\nexport interface SettingsCallbacks {\n\tonAutoCompactChange: (enabled: boolean) => void;\n\tonShowImagesChange: (enabled: boolean) => void;\n\tonImageWidthCellsChange: (width: number) => void;\n\tonAutoResizeImagesChange: (enabled: boolean) => void;\n\tonBlockImagesChange: (blocked: boolean) => void;\n\tonEnableSkillCommandsChange: (enabled: boolean) => void;\n\tonSteeringModeChange: (mode: \"all\" | \"one-at-a-time\") => void;\n\tonFollowUpModeChange: (mode: \"all\" | \"one-at-a-time\") => void;\n\tonTransportChange: (transport: Transport) => void;\n\tonHttpIdleTimeoutMsChange: (timeoutMs: number) => void;\n\tonModelThinkingLevelChange: (provider: string, modelId: string, level: ThinkingLevel) => void;\n\tonModelThinkingLevelRemove: (provider: string, modelId: string) => void;\n\tonThemeChange: (theme: string) => void;\n\tonThemePreview?: (theme: string) => void;\n\tonHideThinkingBlockChange: (hidden: boolean) => void;\n\tonMermaidRenderingModeChange: (mode: MermaidRenderingMode) => void;\n\tonShowCacheMissNoticesChange: (shown: boolean) => void;\n\tonCollapseChangelogChange: (collapsed: boolean) => void;\n\tonSendProviderAttributionChange: (enabled: boolean) => void;\n\tonDoubleEscapeActionChange: (action: \"fork\" | \"tree\" | \"none\") => void;\n\tonTreeFilterModeChange: (mode: \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\") => void;\n\tonShowHardwareCursorChange: (enabled: boolean) => void;\n\tonEditorPaddingXChange: (padding: number) => void;\n\tonOutputPadChange: (padding: 0 | 1) => void;\n\tonAutocompleteMaxVisibleChange: (maxVisible: number) => void;\n\tonQuietStartupChange: (enabled: boolean) => void;\n\tonDefaultProjectTrustChange: (defaultProjectTrust: DefaultProjectTrust) => void;\n\tonPermissionModeChange: (mode: PermissionMode) => void;\n\tonClearOnShrinkChange: (enabled: boolean) => void;\n\tonShowTerminalProgressChange: (enabled: boolean) => void;\n\tonTuiModeChange: (mode: TuiMode) => void;\n\tonFullscreenExitOutputChange: (output: FullscreenExitOutput) => void;\n\tonFullscreenScrollbarChange: (mode: ScrollViewScrollbar) => void;\n\tonFullscreenCopyOnSelectChange: (enabled: boolean) => void;\n\tonWarningsChange: (warnings: WarningSettings) => void;\n\tonCancel: () => void;\n}\n\n/**\n * A submenu component for selecting from a list of options.\n */\nclass WarningSettingsSubmenu extends Container {\n\tprivate settingsList: SettingsList;\n\tprivate state: WarningSettings;\n\n\tconstructor(warnings: WarningSettings, onChange: (warnings: WarningSettings) => void, onCancel: () => void) {\n\t\tsuper();\n\n\t\tthis.state = { ...warnings };\n\n\t\tconst items: SettingItem[] = [\n\t\t\t{\n\t\t\t\tid: \"anthropic-extra-usage\",\n\t\t\t\tlabel: \"Anthropic extra usage\",\n\t\t\t\tdescription: \"Warn when Anthropic subscription auth may use paid extra usage\",\n\t\t\t\tcurrentValue: (this.state.anthropicExtraUsage ?? true) ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t];\n\n\t\tthis.settingsList = new SettingsList(\n\t\t\titems,\n\t\t\tMath.min(items.length, 10),\n\t\t\tgetSettingsListTheme(),\n\t\t\t(id, newValue) => {\n\t\t\t\tswitch (id) {\n\t\t\t\t\tcase \"anthropic-extra-usage\":\n\t\t\t\t\t\tthis.state = { ...this.state, anthropicExtraUsage: newValue === \"true\" };\n\t\t\t\t\t\tonChange({ ...this.state });\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\tonCancel,\n\t\t);\n\n\t\tthis.addChild(this.settingsList);\n\t}\n\n\thandleInput(data: string): void {\n\t\tthis.settingsList.handleInput(data);\n\t}\n}\n\nconst CLEAR_OVERRIDE_VALUE = \"__clear__\";\n\nfunction modelSettingKey(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction modelDisplayLabel(model: Model<any>): string {\n\treturn `${model.id} [${model.provider}]`;\n}\n\nfunction modelThinkingOverridesSummary(overrides: Record<string, ThinkingLevel>): string {\n\tconst count = Object.keys(overrides).length;\n\tif (count === 0) return \"none\";\n\treturn `${count} configured`;\n}\n\nfunction modelItemLabel(model: Model<any>): string {\n\treturn `${model.id} ${theme.fg(\"muted\", `[${model.provider}]`)}`;\n}\n\n/**\n * Permission mode picker, mode list then a confirm step for `bypassPermissions`.\n * The confirm exists because the mode list is arrow-cyclable like every other\n * settings row, so \"allow every tool call\" is otherwise one stray keypress away.\n */\nclass PermissionModeSubmenu extends Container {\n\tprivate inputComponent: Component | undefined;\n\tprivate readonly current: PermissionMode;\n\tprivate readonly origin: PermissionModeOrigin;\n\tprivate readonly onSelect: (mode: PermissionMode) => void;\n\tprivate readonly onDone: (selectedValue?: string) => void;\n\n\tconstructor(\n\t\tcurrent: PermissionMode,\n\t\torigin: PermissionModeOrigin,\n\t\tonSelect: (mode: PermissionMode) => void,\n\t\tonDone: (selectedValue?: string) => void,\n\t) {\n\t\tsuper();\n\t\tthis.current = current;\n\t\tthis.origin = origin;\n\t\tthis.onSelect = onSelect;\n\t\tthis.onDone = onDone;\n\t\tthis.showModeList();\n\t}\n\n\thandleInput(data: string): void {\n\t\tthis.inputComponent?.handleInput?.(data);\n\t}\n\n\tprivate setContent(component: Component): void {\n\t\tthis.clear();\n\t\tthis.addChild(component);\n\t\tthis.inputComponent = component;\n\t}\n\n\tprivate description(): string {\n\t\tconst overridden =\n\t\t\tthis.origin !== \"user\" && this.origin !== \"default\"\n\t\t\t\t? ` Currently set by ${PERMISSION_MODE_OVERRIDE_HINTS[this.origin]}.`\n\t\t\t\t: \"\";\n\t\treturn `Saved to user scope and applied to the next tool call.${overridden}`;\n\t}\n\n\tprivate showModeList(): void {\n\t\tthis.setContent(\n\t\t\tnew SelectSubmenu(\n\t\t\t\t\"Permission mode\",\n\t\t\t\tthis.description(),\n\t\t\t\tPERMISSION_MODES.map((mode) => ({\n\t\t\t\t\tvalue: mode,\n\t\t\t\t\tlabel: mode,\n\t\t\t\t\tdescription: PERMISSION_MODE_DESCRIPTIONS[mode],\n\t\t\t\t})),\n\t\t\t\tthis.current,\n\t\t\t\t(value) => {\n\t\t\t\t\tconst mode = value as PermissionMode;\n\t\t\t\t\tif (mode === \"bypassPermissions\") {\n\t\t\t\t\t\tthis.showBypassConfirm();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tthis.onSelect(mode);\n\t\t\t\t\tthis.onDone(mode);\n\t\t\t\t},\n\t\t\t\t() => this.onDone(),\n\t\t\t),\n\t\t);\n\t}\n\n\tprivate showBypassConfirm(): void {\n\t\tthis.setContent(\n\t\t\tnew SelectSubmenu(\n\t\t\t\t\"Bypass all permission checks?\",\n\t\t\t\t\"Every tool call runs without asking, in every project, until you change this back. Nothing else confines the session: it writes anywhere your account can and reaches any host. Run inside a container or VM if the work is untrusted.\",\n\t\t\t\t[\n\t\t\t\t\t{ value: \"no\", label: `No, keep ${this.current}`, description: \"Return to the mode list\" },\n\t\t\t\t\t{ value: \"yes\", label: \"Yes, bypass\", description: \"Allow every tool call without asking\" },\n\t\t\t\t],\n\t\t\t\t\"no\",\n\t\t\t\t(value) => {\n\t\t\t\t\tif (value !== \"yes\") {\n\t\t\t\t\t\tthis.showModeList();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tthis.onSelect(\"bypassPermissions\");\n\t\t\t\t\tthis.onDone(\"bypassPermissions\");\n\t\t\t\t},\n\t\t\t\t() => this.showModeList(),\n\t\t\t),\n\t\t);\n\t}\n}\n\nfunction themeItems(availableThemes: string[]): SelectItem[] {\n\treturn availableThemes.map((name) => ({ value: name, label: name }));\n}\n\nconst AUTOMATIC_THEME_VALUE = \"/\";\n\nfunction singleModeThemeItems(availableThemes: string[]): SelectItem[] {\n\treturn [\n\t\t{\n\t\t\tvalue: AUTOMATIC_THEME_VALUE,\n\t\t\tlabel: \"Automatic\",\n\t\t\tdescription: \"Use separate themes for light and dark terminal appearance\",\n\t\t},\n\t\t...themeItems(availableThemes),\n\t];\n}\n\nfunction preferredTheme(availableThemes: string[], preferred: string | undefined, fallback: string): string {\n\tif (preferred && availableThemes.includes(preferred)) return preferred;\n\tif (availableThemes.includes(fallback)) return fallback;\n\treturn availableThemes[0] ?? fallback;\n}\n\nfunction defaultAutomaticThemes(\n\tcurrentThemeSetting: string,\n\tavailableThemes: string[],\n): { lightTheme: string; darkTheme: string } {\n\tconst autoTheme = parseAutoThemeSetting(currentThemeSetting);\n\tif (autoTheme) return autoTheme;\n\n\tconst currentFixedTheme = currentThemeSetting.includes(\"/\") ? undefined : currentThemeSetting;\n\tconst themeName = preferredTheme(availableThemes, currentFixedTheme, \"dark\");\n\treturn { lightTheme: themeName, darkTheme: themeName };\n}\n\nclass ThemeSubmenu extends Container {\n\tprivate inputComponent: Component | undefined;\n\tprivate readonly callbacks: SettingsCallbacks;\n\tprivate readonly availableThemes: string[];\n\tprivate readonly terminalTheme: TerminalTheme;\n\tprivate readonly onDone: (selectedValue?: string) => void;\n\tprivate readonly originalThemeSetting: string;\n\tprivate mode: \"single\" | \"automatic\";\n\tprivate singleTheme: string;\n\tprivate lightTheme: string;\n\tprivate darkTheme: string;\n\n\tconstructor(\n\t\tcurrentThemeSetting: string,\n\t\tterminalTheme: TerminalTheme,\n\t\tavailableThemes: string[],\n\t\tcallbacks: SettingsCallbacks,\n\t\tonDone: (selectedValue?: string) => void,\n\t) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\t\tthis.availableThemes = availableThemes;\n\t\tthis.terminalTheme = terminalTheme;\n\t\tthis.onDone = onDone;\n\t\tthis.originalThemeSetting = currentThemeSetting;\n\t\tconst autoTheme = parseAutoThemeSetting(currentThemeSetting);\n\t\tconst automaticThemes = defaultAutomaticThemes(currentThemeSetting, availableThemes);\n\t\tconst fixedTheme = autoTheme || currentThemeSetting.includes(\"/\") ? undefined : currentThemeSetting;\n\t\tthis.mode = autoTheme ? \"automatic\" : \"single\";\n\t\tthis.lightTheme = automaticThemes.lightTheme;\n\t\tthis.darkTheme = automaticThemes.darkTheme;\n\t\tthis.singleTheme = preferredTheme(\n\t\t\tavailableThemes,\n\t\t\tfixedTheme ?? (autoTheme ? this.getActiveAutomaticTheme() : undefined),\n\t\t\t\"dark\",\n\t\t);\n\n\t\tif (this.mode === \"automatic\") {\n\t\t\tthis.showAutomaticMenu();\n\t\t} else {\n\t\t\tthis.showSingleMenu();\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tthis.inputComponent?.handleInput?.(data);\n\t}\n\n\tprivate setContent(renderComponent: Component, inputComponent: Component = renderComponent): void {\n\t\tthis.clear();\n\t\tthis.addChild(renderComponent);\n\t\tthis.inputComponent = inputComponent;\n\t}\n\n\tprivate showSingleMenu(): void {\n\t\tthis.mode = \"single\";\n\t\tconst menu = new SelectSubmenu(\n\t\t\t\"Theme\",\n\t\t\t\"Select a theme, or choose Automatic to follow terminal appearance.\",\n\t\t\tsingleModeThemeItems(this.availableThemes),\n\t\t\tthis.singleTheme,\n\t\t\t(value) => {\n\t\t\t\tif (value === AUTOMATIC_THEME_VALUE) {\n\t\t\t\t\tthis.mode = \"automatic\";\n\t\t\t\t\tthis.callbacks.onThemePreview?.(this.getThemeSetting());\n\t\t\t\t\tthis.showAutomaticMenu();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.singleTheme = value;\n\t\t\t\tthis.apply(value);\n\t\t\t},\n\t\t\t() => this.cancel(),\n\t\t\t(value) => {\n\t\t\t\tthis.callbacks.onThemePreview?.(value === AUTOMATIC_THEME_VALUE ? this.getAutomaticThemeSetting() : value);\n\t\t\t},\n\t\t);\n\t\tthis.setContent(menu);\n\t}\n\n\tprivate showAutomaticMenu(): void {\n\t\tthis.mode = \"automatic\";\n\t\tconst content = new Container();\n\t\tcontent.addChild(new Text(theme.bold(theme.fg(\"accent\", \"Automatic Theme\")), 0, 0));\n\t\tcontent.addChild(new Spacer(1));\n\t\tcontent.addChild(new Text(theme.fg(\"muted\", \"Choose themes for terminal light and dark appearance.\"), 0, 0));\n\t\tcontent.addChild(new Text(theme.fg(\"muted\", \"Light/dark detection requires terminal support.\"), 0, 0));\n\t\tcontent.addChild(new Spacer(1));\n\n\t\tconst items: SettingItem[] = [\n\t\t\t{\n\t\t\t\tid: \"light-theme\",\n\t\t\t\tlabel: \"Light theme\",\n\t\t\t\tdescription: \"Theme to use in automatic mode when the terminal is light\",\n\t\t\t\tcurrentValue: this.lightTheme,\n\t\t\t\tsubmenu: (currentValue, done) =>\n\t\t\t\t\tthis.createThemeSelect(\n\t\t\t\t\t\t\"Light Theme\",\n\t\t\t\t\t\t\"Select the theme to use for light terminal appearance\",\n\t\t\t\t\t\tcurrentValue,\n\t\t\t\t\t\tdone,\n\t\t\t\t\t\t(value) => {\n\t\t\t\t\t\t\tthis.lightTheme = value;\n\t\t\t\t\t\t\tthis.callbacks.onThemePreview?.(this.getThemeSetting());\n\t\t\t\t\t\t\tdone(value);\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"dark-theme\",\n\t\t\t\tlabel: \"Dark theme\",\n\t\t\t\tdescription: \"Theme to use in automatic mode when the terminal is dark\",\n\t\t\t\tcurrentValue: this.darkTheme,\n\t\t\t\tsubmenu: (currentValue, done) =>\n\t\t\t\t\tthis.createThemeSelect(\n\t\t\t\t\t\t\"Dark Theme\",\n\t\t\t\t\t\t\"Select the theme to use for dark terminal appearance\",\n\t\t\t\t\t\tcurrentValue,\n\t\t\t\t\t\tdone,\n\t\t\t\t\t\t(value) => {\n\t\t\t\t\t\t\tthis.darkTheme = value;\n\t\t\t\t\t\t\tthis.callbacks.onThemePreview?.(this.getThemeSetting());\n\t\t\t\t\t\t\tdone(value);\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"apply\",\n\t\t\t\tlabel: \"Apply\",\n\t\t\t\tdescription: \"Save and go back\",\n\t\t\t\tcurrentValue: \"save and go back\",\n\t\t\t\tvalues: [\"save and go back\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"single-mode\",\n\t\t\t\tlabel: \"Change mode\",\n\t\t\t\tdescription: \"Switch to one theme for light and dark\",\n\t\t\t\tcurrentValue: \"switch to single theme\",\n\t\t\t\tvalues: [\"switch to single theme\"],\n\t\t\t},\n\t\t];\n\n\t\tconst settingsList = new SettingsList(\n\t\t\titems,\n\t\t\tMath.min(items.length, 10),\n\t\t\tgetSettingsListTheme(),\n\t\t\t(id) => {\n\t\t\t\tswitch (id) {\n\t\t\t\t\tcase \"single-mode\":\n\t\t\t\t\t\tthis.mode = \"single\";\n\t\t\t\t\t\tthis.singleTheme = this.getActiveAutomaticTheme();\n\t\t\t\t\t\tthis.callbacks.onThemePreview?.(this.singleTheme);\n\t\t\t\t\t\tthis.showSingleMenu();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"apply\":\n\t\t\t\t\t\tthis.apply(this.getAutomaticThemeSetting());\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => this.cancel(),\n\t\t);\n\t\tcontent.addChild(settingsList);\n\t\tthis.setContent(content, settingsList);\n\t}\n\n\tprivate createThemeSelect(\n\t\ttitle: string,\n\t\tdescription: string,\n\t\tcurrentValue: string,\n\t\tdone: (selectedValue?: string) => void,\n\t\tonSelect: (value: string) => void,\n\t): SelectSubmenu {\n\t\treturn new SelectSubmenu(\n\t\t\ttitle,\n\t\t\tdescription,\n\t\t\tthemeItems(this.availableThemes),\n\t\t\tcurrentValue,\n\t\t\tonSelect,\n\t\t\t() => {\n\t\t\t\tthis.callbacks.onThemePreview?.(this.getThemeSetting());\n\t\t\t\tdone();\n\t\t\t},\n\t\t\t(value) => this.callbacks.onThemePreview?.(value),\n\t\t);\n\t}\n\n\tprivate getThemeSetting(): string {\n\t\treturn this.mode === \"automatic\" ? this.getAutomaticThemeSetting() : this.singleTheme;\n\t}\n\n\tprivate getActiveAutomaticTheme(): string {\n\t\treturn this.terminalTheme === \"light\" ? this.lightTheme : this.darkTheme;\n\t}\n\n\tprivate getAutomaticThemeSetting(): string {\n\t\treturn `${this.lightTheme}/${this.darkTheme}`;\n\t}\n\n\tprivate apply(themeSetting: string): void {\n\t\tthis.onDone(themeSetting);\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.callbacks.onThemePreview?.(this.originalThemeSetting);\n\t\tthis.onDone();\n\t}\n}\n\n/**\n * Main settings selector component.\n */\nexport class SettingsSelectorComponent extends Container {\n\tprivate settingsList: SettingsList;\n\n\tconstructor(config: SettingsConfig, callbacks: SettingsCallbacks) {\n\t\tsuper();\n\n\t\tconst supportsImages = getCapabilities().images;\n\t\tconst followUpKey = keyDisplayText(\"app.message.followUp\");\n\t\tconst cycleThinkingKey = keyDisplayText(\"app.thinking.cycle\");\n\t\tlet currentWarnings = { ...config.warnings };\n\t\tconst currentModelThinkingLevels = { ...config.modelThinkingLevels };\n\t\tconst defaultModelByValue = new Map(\n\t\t\tconfig.availableDefaultModels.map((model) => [modelSettingKey(model), model]),\n\t\t);\n\t\tconst currentDefaultModelKey = defaultModelByValue.has(config.defaultModel) ? config.defaultModel : undefined;\n\t\tconst currentModelKey = config.currentModel ? modelSettingKey(config.currentModel) : undefined;\n\n\t\tconst items: SettingItem[] = [\n\t\t\t{\n\t\t\t\tid: \"autocompact\",\n\t\t\t\tlabel: \"Auto-compact\",\n\t\t\t\tdescription: \"Automatically compact context when it gets too large\",\n\t\t\t\tcurrentValue: config.autoCompact ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"steering-mode\",\n\t\t\t\tlabel: \"Steering mode\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Enter while streaming queues steering messages. 'one-at-a-time': deliver one, wait for response. 'all': deliver all at once.\",\n\t\t\t\tcurrentValue: config.steeringMode,\n\t\t\t\tvalues: [\"one-at-a-time\", \"all\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"follow-up-mode\",\n\t\t\t\tlabel: \"Follow-up mode\",\n\t\t\t\tdescription: `${followUpKey} queues follow-up messages until agent stops. 'one-at-a-time': deliver one, wait for response. 'all': deliver all at once.`,\n\t\t\t\tcurrentValue: config.followUpMode,\n\t\t\t\tvalues: [\"one-at-a-time\", \"all\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"transport\",\n\t\t\t\tlabel: \"Transport\",\n\t\t\t\tdescription: \"Preferred transport for providers that support multiple transports\",\n\t\t\t\tcurrentValue: config.transport,\n\t\t\t\tvalues: [\"sse\", \"websocket\", \"websocket-cached\", \"auto\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"http-idle-timeout\",\n\t\t\t\tlabel: \"HTTP idle timeout\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Maximum idle gap while waiting for HTTP headers or body chunks. Disable for local models that pause longer than five minutes.\",\n\t\t\t\tcurrentValue: formatHttpIdleTimeoutMs(config.httpIdleTimeoutMs),\n\t\t\t\tvalues: HTTP_IDLE_TIMEOUT_CHOICES.map((choice) => choice.label),\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"hide-thinking\",\n\t\t\t\tlabel: \"Hide thinking\",\n\t\t\t\tdescription: \"Hide thinking blocks in assistant responses\",\n\t\t\t\tcurrentValue: config.hideThinkingBlock ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"mermaid-rendering\",\n\t\t\t\tlabel: \"Mermaid diagrams\",\n\t\t\t\tdescription: \"Render Mermaid code blocks as Unicode diagrams\",\n\t\t\t\tcurrentValue: config.mermaidRenderingMode,\n\t\t\t\tvalues: [\"off\", \"final\", \"streaming\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"cache-miss-notices\",\n\t\t\t\tlabel: \"Cache miss notices\",\n\t\t\t\tdescription: \"Show transcript notices for significant prompt-cache misses and compaction costs\",\n\t\t\t\tcurrentValue: config.showCacheMissNotices ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"collapse-changelog\",\n\t\t\t\tlabel: \"Collapse changelog\",\n\t\t\t\tdescription: \"Show condensed changelog after updates\",\n\t\t\t\tcurrentValue: config.collapseChangelog ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"quiet-startup\",\n\t\t\t\tlabel: \"Quiet startup\",\n\t\t\t\tdescription: \"Disable verbose printing at startup\",\n\t\t\t\tcurrentValue: config.quietStartup ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"send-provider-attribution\",\n\t\t\t\tlabel: \"Provider attribution headers\",\n\t\t\t\tdescription: \"Identify this CLI to your configured LLM provider (e.g. OpenRouter billing origin)\",\n\t\t\t\tcurrentValue: config.sendProviderAttribution ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"default-project-trust\",\n\t\t\t\tlabel: \"Default project trust\",\n\t\t\t\tdescription: \"Fallback behavior when no extension or saved trust decision decides project trust\",\n\t\t\t\tcurrentValue: DEFAULT_PROJECT_TRUST_LABELS[config.defaultProjectTrust],\n\t\t\t\tvalues: Object.values(DEFAULT_PROJECT_TRUST_LABELS),\n\t\t\t},\n\t\t\t...permissionModeItems(config.permissionMode, callbacks),\n\t\t\t{\n\t\t\t\tid: \"double-escape-action\",\n\t\t\t\tlabel: \"Double-escape action\",\n\t\t\t\tdescription: \"Action when pressing Escape twice with empty editor\",\n\t\t\t\tcurrentValue: config.doubleEscapeAction,\n\t\t\t\tvalues: [\"tree\", \"fork\", \"none\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"tree-filter-mode\",\n\t\t\t\tlabel: \"Tree filter mode\",\n\t\t\t\tdescription: \"Default filter when opening /tree\",\n\t\t\t\tcurrentValue: config.treeFilterMode,\n\t\t\t\tvalues: [\"default\", \"no-tools\", \"user-only\", \"labeled-only\", \"all\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"warnings\",\n\t\t\t\tlabel: \"Warnings\",\n\t\t\t\tdescription: \"Enable or disable individual warnings\",\n\t\t\t\tcurrentValue: \"configure\",\n\t\t\t\tsubmenu: (_currentValue, done) =>\n\t\t\t\t\tnew WarningSettingsSubmenu(\n\t\t\t\t\t\tcurrentWarnings,\n\t\t\t\t\t\t(warnings) => {\n\t\t\t\t\t\t\tcurrentWarnings = warnings;\n\t\t\t\t\t\t\tcallbacks.onWarningsChange(warnings);\n\t\t\t\t\t\t},\n\t\t\t\t\t\t() => done(),\n\t\t\t\t\t),\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"model-thinking\",\n\t\t\t\tlabel: \"Default thinking level per model\",\n\t\t\t\tdescription: `Override the default thinking level for specific models. ${cycleThinkingKey} cycles in-session.`,\n\t\t\t\tcurrentValue: modelThinkingOverridesSummary(currentModelThinkingLevels),\n\t\t\t\tsubmenu: (_currentValue, done) => {\n\t\t\t\t\tconst steps: SteppedSubmenuStep[] = [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkey: \"model\",\n\t\t\t\t\t\t\ttitle: \"Per-Model Thinking Level\",\n\t\t\t\t\t\t\tdescription: \"Select a model to configure\",\n\t\t\t\t\t\t\toptions: () => {\n\t\t\t\t\t\t\t\tconst sorted = [...config.availableDefaultModels].sort((a, b) => {\n\t\t\t\t\t\t\t\t\tconst aKey = modelSettingKey(a);\n\t\t\t\t\t\t\t\t\tconst bKey = modelSettingKey(b);\n\t\t\t\t\t\t\t\t\tif (aKey === currentModelKey) return -1;\n\t\t\t\t\t\t\t\t\tif (bKey === currentModelKey) return 1;\n\t\t\t\t\t\t\t\t\tif (aKey === currentDefaultModelKey) return -1;\n\t\t\t\t\t\t\t\t\tif (bKey === currentDefaultModelKey) return 1;\n\t\t\t\t\t\t\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tconst items: SelectItem[] = sorted.map((model) => {\n\t\t\t\t\t\t\t\t\tconst key = modelSettingKey(model);\n\t\t\t\t\t\t\t\t\tconst override = currentModelThinkingLevels[key];\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\tvalue: key,\n\t\t\t\t\t\t\t\t\t\tlabel: modelItemLabel(model),\n\t\t\t\t\t\t\t\t\t\tdescription: override ?? undefined,\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tif (items.length === 0) {\n\t\t\t\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\t\t\t\tvalue: \"__none__\",\n\t\t\t\t\t\t\t\t\t\tlabel: \"No models available\",\n\t\t\t\t\t\t\t\t\t\tdescription: \"Log in to a provider or configure an API key first\",\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn items;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpreselect: () => currentModelKey ?? currentDefaultModelKey,\n\t\t\t\t\t\t\tsearchable: true,\n\t\t\t\t\t\t\tlayout: MODEL_PICKER_LAYOUT,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkey: \"level\",\n\t\t\t\t\t\t\ttitle: (ctx) => {\n\t\t\t\t\t\t\t\tconst m = defaultModelByValue.get(ctx.model);\n\t\t\t\t\t\t\t\treturn `Thinking Level for ${m ? modelDisplayLabel(m) : ctx.model}`;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tdescription: \"Select default thinking level for this model\",\n\t\t\t\t\t\t\toptions: (ctx) => {\n\t\t\t\t\t\t\t\tconst model = defaultModelByValue.get(ctx.model);\n\t\t\t\t\t\t\t\tif (!model) return [];\n\t\t\t\t\t\t\t\tconst levels = (\n\t\t\t\t\t\t\t\t\tmodel.reasoning ? getSupportedThinkingLevels(model) : [\"off\"]\n\t\t\t\t\t\t\t\t) as ThinkingLevel[];\n\t\t\t\t\t\t\t\tconst items: SelectItem[] = levels.map((level) => ({\n\t\t\t\t\t\t\t\t\tvalue: level,\n\t\t\t\t\t\t\t\t\tlabel: level,\n\t\t\t\t\t\t\t\t\tdescription: THINKING_DESCRIPTIONS[level],\n\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\tif (currentModelThinkingLevels[ctx.model] !== undefined) {\n\t\t\t\t\t\t\t\t\titems.push({\n\t\t\t\t\t\t\t\t\t\tvalue: CLEAR_OVERRIDE_VALUE,\n\t\t\t\t\t\t\t\t\t\tlabel: \"(clear override)\",\n\t\t\t\t\t\t\t\t\t\tdescription: `Revert to global default (${config.thinkingLevel})`,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn items;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tpreselect: (ctx) => currentModelThinkingLevels[ctx.model],\n\t\t\t\t\t\t},\n\t\t\t\t\t];\n\n\t\t\t\t\tconst summary = () => modelThinkingOverridesSummary(currentModelThinkingLevels);\n\n\t\t\t\t\treturn new SteppedSubmenu(\n\t\t\t\t\t\tsteps,\n\t\t\t\t\t\t(selections) => {\n\t\t\t\t\t\t\tconst model = defaultModelByValue.get(selections.model);\n\t\t\t\t\t\t\tif (!model) return;\n\t\t\t\t\t\t\tif (selections.level === CLEAR_OVERRIDE_VALUE) {\n\t\t\t\t\t\t\t\tcallbacks.onModelThinkingLevelRemove(model.provider, model.id);\n\t\t\t\t\t\t\t\tdelete currentModelThinkingLevels[selections.model];\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcallbacks.onModelThinkingLevelChange(\n\t\t\t\t\t\t\t\t\tmodel.provider,\n\t\t\t\t\t\t\t\t\tmodel.id,\n\t\t\t\t\t\t\t\t\tselections.level as ThinkingLevel,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tcurrentModelThinkingLevels[selections.model] = selections.level as ThinkingLevel;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t() => {\n\t\t\t\t\t\t\tdone(summary());\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{ loop: true },\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"tui-mode\",\n\t\t\t\tlabel: \"TUI mode\",\n\t\t\t\tdescription: \"Interface layout; fullscreen mode is experimental\",\n\t\t\t\tcurrentValue: config.tuiMode,\n\t\t\t\tvalues: [\"regular\", \"fullscreen\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"fullscreen-exit-output\",\n\t\t\t\tlabel: \"Fullscreen exit output\",\n\t\t\t\tdescription: \"Print the transcript or only a session resume hint when exiting fullscreen mode\",\n\t\t\t\tcurrentValue: config.fullscreenExitOutput,\n\t\t\t\tvalues: [\"transcript\", \"resume-hint\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"fullscreen-scrollbar\",\n\t\t\t\tlabel: \"Fullscreen scrollbar\",\n\t\t\t\tdescription: \"Scrollbar behavior in fullscreen mode; has no effect in regular mode\",\n\t\t\t\tcurrentValue: config.fullscreenScrollbar,\n\t\t\t\tvalues: [\"auto\", \"always\", \"hidden\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"fullscreen-copy-on-select\",\n\t\t\t\tlabel: \"Fullscreen copy on select\",\n\t\t\t\tdescription: \"Automatically copy selected text in fullscreen mode; disable to copy selections with Ctrl+X\",\n\t\t\t\tcurrentValue: config.fullscreenCopyOnSelect ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: \"theme\",\n\t\t\t\tlabel: \"Theme\",\n\t\t\t\tdescription: \"Color theme for the interface\",\n\t\t\t\tcurrentValue: config.currentTheme,\n\t\t\t\tsubmenu: (currentValue, done) =>\n\t\t\t\t\tnew ThemeSubmenu(currentValue, config.terminalTheme, config.availableThemes, callbacks, done),\n\t\t\t},\n\t\t];\n\n\t\t// Only show image toggle if terminal supports it\n\t\tif (supportsImages) {\n\t\t\t// Insert after autocompact\n\t\t\titems.splice(1, 0, {\n\t\t\t\tid: \"show-images\",\n\t\t\t\tlabel: \"Show images\",\n\t\t\t\tdescription: \"Render images inline in terminal\",\n\t\t\t\tcurrentValue: config.showImages ? \"true\" : \"false\",\n\t\t\t\tvalues: [\"true\", \"false\"],\n\t\t\t});\n\t\t\titems.splice(2, 0, {\n\t\t\t\tid: \"image-width-cells\",\n\t\t\t\tlabel: \"Image width\",\n\t\t\t\tdescription: \"Preferred inline image width in terminal cells\",\n\t\t\t\tcurrentValue: String(config.imageWidthCells),\n\t\t\t\tvalues: [\"60\", \"80\", \"120\"],\n\t\t\t});\n\t\t}\n\n\t\t// Image auto-resize toggle (always available, affects both attached and read images)\n\t\titems.splice(supportsImages ? 3 : 1, 0, {\n\t\t\tid: \"auto-resize-images\",\n\t\t\tlabel: \"Auto-resize images\",\n\t\t\tdescription: \"Resize large images to 2000x2000 max for better model compatibility\",\n\t\t\tcurrentValue: config.autoResizeImages ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Block images toggle (always available, insert after auto-resize-images)\n\t\tconst autoResizeIndex = items.findIndex((item) => item.id === \"auto-resize-images\");\n\t\titems.splice(autoResizeIndex + 1, 0, {\n\t\t\tid: \"block-images\",\n\t\t\tlabel: \"Block images\",\n\t\t\tdescription: \"Prevent images from being sent to LLM providers\",\n\t\t\tcurrentValue: config.blockImages ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Skill commands toggle (insert after block-images)\n\t\tconst blockImagesIndex = items.findIndex((item) => item.id === \"block-images\");\n\t\titems.splice(blockImagesIndex + 1, 0, {\n\t\t\tid: \"skill-commands\",\n\t\t\tlabel: \"Skill commands\",\n\t\t\tdescription: \"Register skills as /skill:name commands\",\n\t\t\tcurrentValue: config.enableSkillCommands ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Hardware cursor toggle (insert after skill-commands)\n\t\tconst skillCommandsIndex = items.findIndex((item) => item.id === \"skill-commands\");\n\t\titems.splice(skillCommandsIndex + 1, 0, {\n\t\t\tid: \"show-hardware-cursor\",\n\t\t\tlabel: \"Show hardware cursor\",\n\t\t\tdescription: \"Show the terminal cursor while still positioning it for IME support\",\n\t\t\tcurrentValue: config.showHardwareCursor ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Editor padding toggle (insert after show-hardware-cursor)\n\t\tconst hardwareCursorIndex = items.findIndex((item) => item.id === \"show-hardware-cursor\");\n\t\titems.splice(hardwareCursorIndex + 1, 0, {\n\t\t\tid: \"editor-padding\",\n\t\t\tlabel: \"Editor padding\",\n\t\t\tdescription: \"Horizontal padding for input editor (0-3)\",\n\t\t\tcurrentValue: String(config.editorPaddingX),\n\t\t\tvalues: [\"0\", \"1\", \"2\", \"3\"],\n\t\t});\n\n\t\t// Output padding toggle (insert after editor-padding)\n\t\tconst editorPaddingIndex = items.findIndex((item) => item.id === \"editor-padding\");\n\t\titems.splice(editorPaddingIndex + 1, 0, {\n\t\t\tid: \"output-padding\",\n\t\t\tlabel: \"Output padding\",\n\t\t\tdescription: \"Horizontal padding for user messages, assistant messages, and thinking\",\n\t\t\tcurrentValue: String(config.outputPad),\n\t\t\tvalues: [\"0\", \"1\"],\n\t\t});\n\n\t\t// Autocomplete max visible toggle (insert after output-padding)\n\t\tconst outputPaddingIndex = items.findIndex((item) => item.id === \"output-padding\");\n\t\titems.splice(outputPaddingIndex + 1, 0, {\n\t\t\tid: \"autocomplete-max-visible\",\n\t\t\tlabel: \"Autocomplete max items\",\n\t\t\tdescription: \"Max visible items in autocomplete dropdown (3-20)\",\n\t\t\tcurrentValue: String(config.autocompleteMaxVisible),\n\t\t\tvalues: [\"3\", \"5\", \"7\", \"10\", \"15\", \"20\"],\n\t\t});\n\n\t\t// Clear on shrink toggle (insert after autocomplete-max-visible)\n\t\tconst autocompleteIndex = items.findIndex((item) => item.id === \"autocomplete-max-visible\");\n\t\titems.splice(autocompleteIndex + 1, 0, {\n\t\t\tid: \"clear-on-shrink\",\n\t\t\tlabel: \"Clear on shrink\",\n\t\t\tdescription: \"Clear empty rows when content shrinks (may cause flicker)\",\n\t\t\tcurrentValue: config.clearOnShrink ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Terminal progress toggle (insert after clear-on-shrink)\n\t\tconst clearOnShrinkIndex = items.findIndex((item) => item.id === \"clear-on-shrink\");\n\t\titems.splice(clearOnShrinkIndex + 1, 0, {\n\t\t\tid: \"terminal-progress\",\n\t\t\tlabel: \"Terminal progress\",\n\t\t\tdescription: \"Show OSC 9;4 progress indicators in the terminal tab bar\",\n\t\t\tcurrentValue: config.showTerminalProgress ? \"true\" : \"false\",\n\t\t\tvalues: [\"true\", \"false\"],\n\t\t});\n\n\t\t// Add borders\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.settingsList = new SettingsList(\n\t\t\titems,\n\t\t\t10,\n\t\t\tgetSettingsListTheme(),\n\t\t\t(id, newValue) => {\n\t\t\t\tswitch (id) {\n\t\t\t\t\tcase \"autocompact\":\n\t\t\t\t\t\tcallbacks.onAutoCompactChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"show-images\":\n\t\t\t\t\t\tcallbacks.onShowImagesChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"image-width-cells\":\n\t\t\t\t\t\tcallbacks.onImageWidthCellsChange(parseInt(newValue, 10));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"auto-resize-images\":\n\t\t\t\t\t\tcallbacks.onAutoResizeImagesChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"block-images\":\n\t\t\t\t\t\tcallbacks.onBlockImagesChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"skill-commands\":\n\t\t\t\t\t\tcallbacks.onEnableSkillCommandsChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"steering-mode\":\n\t\t\t\t\t\tcallbacks.onSteeringModeChange(newValue as \"all\" | \"one-at-a-time\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"follow-up-mode\":\n\t\t\t\t\t\tcallbacks.onFollowUpModeChange(newValue as \"all\" | \"one-at-a-time\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"transport\":\n\t\t\t\t\t\tcallbacks.onTransportChange(newValue as Transport);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"http-idle-timeout\": {\n\t\t\t\t\t\tconst choice = HTTP_IDLE_TIMEOUT_CHOICES.find((item) => item.label === newValue);\n\t\t\t\t\t\tif (choice) {\n\t\t\t\t\t\t\tcallbacks.onHttpIdleTimeoutMsChange(choice.timeoutMs);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"hide-thinking\":\n\t\t\t\t\t\tcallbacks.onHideThinkingBlockChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"mermaid-rendering\":\n\t\t\t\t\t\tcallbacks.onMermaidRenderingModeChange(newValue as MermaidRenderingMode);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"cache-miss-notices\":\n\t\t\t\t\t\tcallbacks.onShowCacheMissNoticesChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"collapse-changelog\":\n\t\t\t\t\t\tcallbacks.onCollapseChangelogChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"quiet-startup\":\n\t\t\t\t\t\tcallbacks.onQuietStartupChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"send-provider-attribution\":\n\t\t\t\t\t\tcallbacks.onSendProviderAttributionChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"default-project-trust\": {\n\t\t\t\t\t\tconst defaultProjectTrust = DEFAULT_PROJECT_TRUST_BY_LABEL.get(newValue);\n\t\t\t\t\t\tif (defaultProjectTrust) {\n\t\t\t\t\t\t\tcallbacks.onDefaultProjectTrustChange(defaultProjectTrust);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"double-escape-action\":\n\t\t\t\t\t\tcallbacks.onDoubleEscapeActionChange(newValue as \"fork\" | \"tree\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"tree-filter-mode\":\n\t\t\t\t\t\tcallbacks.onTreeFilterModeChange(\n\t\t\t\t\t\t\tnewValue as \"default\" | \"no-tools\" | \"user-only\" | \"labeled-only\" | \"all\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"show-hardware-cursor\":\n\t\t\t\t\t\tcallbacks.onShowHardwareCursorChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"editor-padding\":\n\t\t\t\t\t\tcallbacks.onEditorPaddingXChange(parseInt(newValue, 10));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"output-padding\":\n\t\t\t\t\t\tcallbacks.onOutputPadChange(newValue === \"0\" ? 0 : 1);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"autocomplete-max-visible\":\n\t\t\t\t\t\tcallbacks.onAutocompleteMaxVisibleChange(parseInt(newValue, 10));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"clear-on-shrink\":\n\t\t\t\t\t\tcallbacks.onClearOnShrinkChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"terminal-progress\":\n\t\t\t\t\t\tcallbacks.onShowTerminalProgressChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"tui-mode\":\n\t\t\t\t\t\tcallbacks.onTuiModeChange(newValue as TuiMode);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"fullscreen-exit-output\":\n\t\t\t\t\t\tcallbacks.onFullscreenExitOutputChange(newValue as FullscreenExitOutput);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"fullscreen-scrollbar\":\n\t\t\t\t\t\tcallbacks.onFullscreenScrollbarChange(newValue as ScrollViewScrollbar);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"fullscreen-copy-on-select\":\n\t\t\t\t\t\tcallbacks.onFullscreenCopyOnSelectChange(newValue === \"true\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"theme\":\n\t\t\t\t\t\tcallbacks.onThemeChange(newValue);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\tcallbacks.onCancel,\n\t\t\t{ enableSearch: true },\n\t\t);\n\n\t\tthis.addChild(this.settingsList);\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tgetSettingsList(): SettingsList {\n\t\treturn this.settingsList;\n\t}\n}\n"]}
@@ -151,7 +151,7 @@ class PermissionModeSubmenu extends Container {
151
151
  }, () => this.onDone()));
152
152
  }
153
153
  showBypassConfirm() {
154
- this.setContent(new SelectSubmenu("Bypass all permission checks?", "Every tool call runs without asking, in every project, until you change this back. The OS sandbox still confines writes to the workspace and network egress to the allowlist.", [
154
+ this.setContent(new SelectSubmenu("Bypass all permission checks?", "Every tool call runs without asking, in every project, until you change this back. Nothing else confines the session: it writes anywhere your account can and reaches any host. Run inside a container or VM if the work is untrusted.", [
155
155
  { value: "no", label: `No, keep ${this.current}`, description: "Return to the mode list" },
156
156
  { value: "yes", label: "Yes, bypass", description: "Allow every tool call without asking" },
157
157
  ], "no", (value) => {