apex-code 0.0.1-alpha.1 → 0.0.1-alpha.11

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 (798) hide show
  1. package/CHANGELOG.md +482 -0
  2. package/README.md +75 -680
  3. package/dist/cli/args.d.ts +21 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +119 -15
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/auth-command.d.ts.map +1 -1
  8. package/dist/cli/auth-command.js +3 -3
  9. package/dist/cli/auth-command.js.map +1 -1
  10. package/dist/cli/concurrent-session.d.ts +3 -0
  11. package/dist/cli/concurrent-session.d.ts.map +1 -0
  12. package/dist/cli/concurrent-session.js +13 -0
  13. package/dist/cli/concurrent-session.js.map +1 -0
  14. package/dist/cli/cost-command.d.ts +19 -0
  15. package/dist/cli/cost-command.d.ts.map +1 -0
  16. package/dist/cli/cost-command.js +107 -0
  17. package/dist/cli/cost-command.js.map +1 -0
  18. package/dist/cli/file-processor.d.ts.map +1 -1
  19. package/dist/cli/file-processor.js +2 -1
  20. package/dist/cli/file-processor.js.map +1 -1
  21. package/dist/cli/startup-ui.d.ts +1 -1
  22. package/dist/cli/startup-ui.d.ts.map +1 -1
  23. package/dist/cli/startup-ui.js +1 -2
  24. package/dist/cli/startup-ui.js.map +1 -1
  25. package/dist/cli.d.ts.map +1 -1
  26. package/dist/cli.js +99 -16
  27. package/dist/cli.js.map +1 -1
  28. package/dist/config.d.ts +5 -4
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +58 -24
  31. package/dist/config.js.map +1 -1
  32. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  33. package/dist/core/agent-session-runtime.js +2 -0
  34. package/dist/core/agent-session-runtime.js.map +1 -1
  35. package/dist/core/agent-session-services.d.ts +28 -0
  36. package/dist/core/agent-session-services.d.ts.map +1 -1
  37. package/dist/core/agent-session-services.js +140 -4
  38. package/dist/core/agent-session-services.js.map +1 -1
  39. package/dist/core/agent-session.d.ts +147 -9
  40. package/dist/core/agent-session.d.ts.map +1 -1
  41. package/dist/core/agent-session.js +440 -123
  42. package/dist/core/agent-session.js.map +1 -1
  43. package/dist/core/auth-storage.d.ts +4 -1
  44. package/dist/core/auth-storage.d.ts.map +1 -1
  45. package/dist/core/auth-storage.js +17 -14
  46. package/dist/core/auth-storage.js.map +1 -1
  47. package/dist/core/bash-executor.d.ts.map +1 -1
  48. package/dist/core/bash-executor.js +18 -10
  49. package/dist/core/bash-executor.js.map +1 -1
  50. package/dist/core/checkpoints/git-checkpoints.d.ts +33 -0
  51. package/dist/core/checkpoints/git-checkpoints.d.ts.map +1 -0
  52. package/dist/core/checkpoints/git-checkpoints.js +230 -0
  53. package/dist/core/checkpoints/git-checkpoints.js.map +1 -0
  54. package/dist/core/checkpoints/session-checkpoints.d.ts +23 -0
  55. package/dist/core/checkpoints/session-checkpoints.d.ts.map +1 -0
  56. package/dist/core/checkpoints/session-checkpoints.js +29 -0
  57. package/dist/core/checkpoints/session-checkpoints.js.map +1 -0
  58. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  59. package/dist/core/compaction/branch-summarization.js +3 -0
  60. package/dist/core/compaction/branch-summarization.js.map +1 -1
  61. package/dist/core/compaction/compaction.d.ts +4 -3
  62. package/dist/core/compaction/compaction.d.ts.map +1 -1
  63. package/dist/core/compaction/compaction.js +41 -32
  64. package/dist/core/compaction/compaction.js.map +1 -1
  65. package/dist/core/context/deferred-schemas.d.ts +92 -0
  66. package/dist/core/context/deferred-schemas.d.ts.map +1 -0
  67. package/dist/core/context/deferred-schemas.js +74 -0
  68. package/dist/core/context/deferred-schemas.js.map +1 -0
  69. package/dist/core/context/eviction.d.ts +47 -0
  70. package/dist/core/context/eviction.d.ts.map +1 -0
  71. package/dist/core/context/eviction.js +83 -0
  72. package/dist/core/context/eviction.js.map +1 -0
  73. package/dist/core/context/pipeline.d.ts +90 -0
  74. package/dist/core/context/pipeline.d.ts.map +1 -0
  75. package/dist/core/context/pipeline.js +109 -0
  76. package/dist/core/context/pipeline.js.map +1 -0
  77. package/dist/core/credential-failover.d.ts +30 -0
  78. package/dist/core/credential-failover.d.ts.map +1 -0
  79. package/dist/core/credential-failover.js +46 -0
  80. package/dist/core/credential-failover.js.map +1 -0
  81. package/dist/core/credential-pool.d.ts +60 -0
  82. package/dist/core/credential-pool.d.ts.map +1 -0
  83. package/dist/core/credential-pool.js +84 -0
  84. package/dist/core/credential-pool.js.map +1 -0
  85. package/dist/core/defaults.d.ts +1 -0
  86. package/dist/core/defaults.d.ts.map +1 -1
  87. package/dist/core/defaults.js +9 -0
  88. package/dist/core/defaults.js.map +1 -1
  89. package/dist/core/delegation/agents.d.ts +15 -0
  90. package/dist/core/delegation/agents.d.ts.map +1 -0
  91. package/dist/core/delegation/agents.js +55 -0
  92. package/dist/core/delegation/agents.js.map +1 -0
  93. package/dist/core/delegation/ceiling.d.ts +29 -0
  94. package/dist/core/delegation/ceiling.d.ts.map +1 -0
  95. package/dist/core/delegation/ceiling.js +34 -0
  96. package/dist/core/delegation/ceiling.js.map +1 -0
  97. package/dist/core/delegation/runtime.d.ts +94 -0
  98. package/dist/core/delegation/runtime.d.ts.map +1 -0
  99. package/dist/core/delegation/runtime.js +117 -0
  100. package/dist/core/delegation/runtime.js.map +1 -0
  101. package/dist/core/durable-state/daemon.d.ts +37 -0
  102. package/dist/core/durable-state/daemon.d.ts.map +1 -0
  103. package/dist/core/durable-state/daemon.js +62 -0
  104. package/dist/core/durable-state/daemon.js.map +1 -0
  105. package/dist/core/durable-state/provenance.d.ts +7 -0
  106. package/dist/core/durable-state/provenance.d.ts.map +1 -0
  107. package/dist/core/durable-state/provenance.js +24 -0
  108. package/dist/core/durable-state/provenance.js.map +1 -0
  109. package/dist/core/durable-state/sqlite.d.ts +84 -0
  110. package/dist/core/durable-state/sqlite.d.ts.map +1 -0
  111. package/dist/core/durable-state/sqlite.js +355 -0
  112. package/dist/core/durable-state/sqlite.js.map +1 -0
  113. package/dist/core/environment.d.ts +12 -0
  114. package/dist/core/environment.d.ts.map +1 -0
  115. package/dist/core/environment.js +69 -0
  116. package/dist/core/environment.js.map +1 -0
  117. package/dist/core/evidence-policy.d.ts +17 -0
  118. package/dist/core/evidence-policy.d.ts.map +1 -0
  119. package/dist/core/evidence-policy.js +25 -0
  120. package/dist/core/evidence-policy.js.map +1 -0
  121. package/dist/core/evidence.d.ts +16 -0
  122. package/dist/core/evidence.d.ts.map +1 -0
  123. package/dist/core/evidence.js +51 -0
  124. package/dist/core/evidence.js.map +1 -0
  125. package/dist/core/exec.d.ts.map +1 -1
  126. package/dist/core/exec.js +5 -2
  127. package/dist/core/exec.js.map +1 -1
  128. package/dist/core/experimental.d.ts +4 -0
  129. package/dist/core/experimental.d.ts.map +1 -1
  130. package/dist/core/experimental.js +6 -1
  131. package/dist/core/experimental.js.map +1 -1
  132. package/dist/core/extensions/index.d.ts +2 -2
  133. package/dist/core/extensions/index.d.ts.map +1 -1
  134. package/dist/core/extensions/index.js +1 -1
  135. package/dist/core/extensions/index.js.map +1 -1
  136. package/dist/core/extensions/loader.d.ts.map +1 -1
  137. package/dist/core/extensions/loader.js +122 -49
  138. package/dist/core/extensions/loader.js.map +1 -1
  139. package/dist/core/extensions/types.d.ts +44 -9
  140. package/dist/core/extensions/types.d.ts.map +1 -1
  141. package/dist/core/extensions/types.js +3 -0
  142. package/dist/core/extensions/types.js.map +1 -1
  143. package/dist/core/footer-data-provider.d.ts +1 -1
  144. package/dist/core/footer-data-provider.d.ts.map +1 -1
  145. package/dist/core/footer-data-provider.js +1 -1
  146. package/dist/core/footer-data-provider.js.map +1 -1
  147. package/dist/core/http-dispatcher.d.ts +1 -2
  148. package/dist/core/http-dispatcher.d.ts.map +1 -1
  149. package/dist/core/http-dispatcher.js +2 -17
  150. package/dist/core/http-dispatcher.js.map +1 -1
  151. package/dist/core/http-idle-timeout.d.ts +12 -0
  152. package/dist/core/http-idle-timeout.d.ts.map +1 -0
  153. package/dist/core/http-idle-timeout.js +27 -0
  154. package/dist/core/http-idle-timeout.js.map +1 -0
  155. package/dist/core/index.d.ts +1 -0
  156. package/dist/core/index.d.ts.map +1 -1
  157. package/dist/core/index.js +1 -0
  158. package/dist/core/index.js.map +1 -1
  159. package/dist/core/keybindings.d.ts +38 -13
  160. package/dist/core/keybindings.d.ts.map +1 -1
  161. package/dist/core/keybindings.js +26 -5
  162. package/dist/core/keybindings.js.map +1 -1
  163. package/dist/core/lsp/client.d.ts +78 -0
  164. package/dist/core/lsp/client.d.ts.map +1 -0
  165. package/dist/core/lsp/client.js +494 -0
  166. package/dist/core/lsp/client.js.map +1 -0
  167. package/dist/core/lsp/diagnostics.d.ts +47 -0
  168. package/dist/core/lsp/diagnostics.d.ts.map +1 -0
  169. package/dist/core/lsp/diagnostics.js +224 -0
  170. package/dist/core/lsp/diagnostics.js.map +1 -0
  171. package/dist/core/lsp/pool.d.ts +48 -0
  172. package/dist/core/lsp/pool.d.ts.map +1 -0
  173. package/dist/core/lsp/pool.js +150 -0
  174. package/dist/core/lsp/pool.js.map +1 -0
  175. package/dist/core/lsp/registry.d.ts +49 -0
  176. package/dist/core/lsp/registry.d.ts.map +1 -0
  177. package/dist/core/lsp/registry.js +215 -0
  178. package/dist/core/lsp/registry.js.map +1 -0
  179. package/dist/core/lsp/status.d.ts +23 -0
  180. package/dist/core/lsp/status.d.ts.map +1 -0
  181. package/dist/core/lsp/status.js +35 -0
  182. package/dist/core/lsp/status.js.map +1 -0
  183. package/dist/core/lsp/sync.d.ts +13 -0
  184. package/dist/core/lsp/sync.d.ts.map +1 -0
  185. package/dist/core/lsp/sync.js +71 -0
  186. package/dist/core/lsp/sync.js.map +1 -0
  187. package/dist/core/mcp/config.d.ts +16 -0
  188. package/dist/core/mcp/config.d.ts.map +1 -0
  189. package/dist/core/mcp/config.js +152 -0
  190. package/dist/core/mcp/config.js.map +1 -0
  191. package/dist/core/mcp/connector.d.ts +10 -0
  192. package/dist/core/mcp/connector.d.ts.map +1 -0
  193. package/dist/core/mcp/connector.js +70 -0
  194. package/dist/core/mcp/connector.js.map +1 -0
  195. package/dist/core/mcp/contract.d.ts +19 -0
  196. package/dist/core/mcp/contract.d.ts.map +1 -0
  197. package/dist/core/mcp/contract.js +89 -0
  198. package/dist/core/mcp/contract.js.map +1 -0
  199. package/dist/core/mcp/mcp-tool.d.ts +32 -0
  200. package/dist/core/mcp/mcp-tool.d.ts.map +1 -0
  201. package/dist/core/mcp/mcp-tool.js +165 -0
  202. package/dist/core/mcp/mcp-tool.js.map +1 -0
  203. package/dist/core/mcp/metadata-cache.d.ts +29 -0
  204. package/dist/core/mcp/metadata-cache.d.ts.map +1 -0
  205. package/dist/core/mcp/metadata-cache.js +99 -0
  206. package/dist/core/mcp/metadata-cache.js.map +1 -0
  207. package/dist/core/mcp/runtime.d.ts +18 -0
  208. package/dist/core/mcp/runtime.d.ts.map +1 -0
  209. package/dist/core/mcp/runtime.js +35 -0
  210. package/dist/core/mcp/runtime.js.map +1 -0
  211. package/dist/core/mcp/schema.d.ts +22 -0
  212. package/dist/core/mcp/schema.d.ts.map +1 -0
  213. package/dist/core/mcp/schema.js +20 -0
  214. package/dist/core/mcp/schema.js.map +1 -0
  215. package/dist/core/mcp/server-manager.d.ts +43 -0
  216. package/dist/core/mcp/server-manager.d.ts.map +1 -0
  217. package/dist/core/mcp/server-manager.js +142 -0
  218. package/dist/core/mcp/server-manager.js.map +1 -0
  219. package/dist/core/mcp/types.d.ts +98 -0
  220. package/dist/core/mcp/types.d.ts.map +1 -0
  221. package/dist/core/mcp/types.js +8 -0
  222. package/dist/core/mcp/types.js.map +1 -0
  223. package/dist/core/model-config.d.ts +18 -0
  224. package/dist/core/model-config.d.ts.map +1 -1
  225. package/dist/core/model-config.js +25 -6
  226. package/dist/core/model-config.js.map +1 -1
  227. package/dist/core/model-resolver.d.ts +21 -0
  228. package/dist/core/model-resolver.d.ts.map +1 -1
  229. package/dist/core/model-resolver.js +73 -8
  230. package/dist/core/model-resolver.js.map +1 -1
  231. package/dist/core/model-runtime.d.ts +73 -0
  232. package/dist/core/model-runtime.d.ts.map +1 -1
  233. package/dist/core/model-runtime.js +225 -8
  234. package/dist/core/model-runtime.js.map +1 -1
  235. package/dist/core/models-store.d.ts.map +1 -1
  236. package/dist/core/models-store.js +2 -1
  237. package/dist/core/models-store.js.map +1 -1
  238. package/dist/core/observability/aggregate.d.ts +21 -0
  239. package/dist/core/observability/aggregate.d.ts.map +1 -0
  240. package/dist/core/observability/aggregate.js +36 -0
  241. package/dist/core/observability/aggregate.js.map +1 -0
  242. package/dist/core/observability/otlp.d.ts +66 -0
  243. package/dist/core/observability/otlp.d.ts.map +1 -0
  244. package/dist/core/observability/otlp.js +95 -0
  245. package/dist/core/observability/otlp.js.map +1 -0
  246. package/dist/core/package-manager.d.ts.map +1 -1
  247. package/dist/core/package-manager.js +40 -16
  248. package/dist/core/package-manager.js.map +1 -1
  249. package/dist/core/permissions/gate.d.ts +42 -0
  250. package/dist/core/permissions/gate.d.ts.map +1 -0
  251. package/dist/core/permissions/gate.js +72 -0
  252. package/dist/core/permissions/gate.js.map +1 -0
  253. package/dist/core/permissions/modes.d.ts +18 -0
  254. package/dist/core/permissions/modes.d.ts.map +1 -0
  255. package/dist/core/permissions/modes.js +57 -0
  256. package/dist/core/permissions/modes.js.map +1 -0
  257. package/dist/core/permissions/responder.d.ts +30 -0
  258. package/dist/core/permissions/responder.d.ts.map +1 -0
  259. package/dist/core/permissions/responder.js +32 -0
  260. package/dist/core/permissions/responder.js.map +1 -0
  261. package/dist/core/permissions/rules.d.ts +34 -0
  262. package/dist/core/permissions/rules.d.ts.map +1 -0
  263. package/dist/core/permissions/rules.js +74 -0
  264. package/dist/core/permissions/rules.js.map +1 -0
  265. package/dist/core/permissions/startup.d.ts +45 -0
  266. package/dist/core/permissions/startup.d.ts.map +1 -0
  267. package/dist/core/permissions/startup.js +63 -0
  268. package/dist/core/permissions/startup.js.map +1 -0
  269. package/dist/core/permissions/store.d.ts +114 -0
  270. package/dist/core/permissions/store.d.ts.map +1 -0
  271. package/dist/core/permissions/store.js +229 -0
  272. package/dist/core/permissions/store.js.map +1 -0
  273. package/dist/core/pi-manifest.d.ts.map +1 -1
  274. package/dist/core/pi-manifest.js +2 -1
  275. package/dist/core/pi-manifest.js.map +1 -1
  276. package/dist/core/project-trust.d.ts.map +1 -1
  277. package/dist/core/project-trust.js +1 -1
  278. package/dist/core/project-trust.js.map +1 -1
  279. package/dist/core/provider-attribution.d.ts.map +1 -1
  280. package/dist/core/provider-attribution.js +6 -7
  281. package/dist/core/provider-attribution.js.map +1 -1
  282. package/dist/core/remote-catalog-provider.d.ts +2 -2
  283. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  284. package/dist/core/remote-catalog-provider.js +22 -17
  285. package/dist/core/remote-catalog-provider.js.map +1 -1
  286. package/dist/core/resource-loader.d.ts.map +1 -1
  287. package/dist/core/resource-loader.js +3 -2
  288. package/dist/core/resource-loader.js.map +1 -1
  289. package/dist/core/sandbox/bwrap-arguments.d.ts +52 -0
  290. package/dist/core/sandbox/bwrap-arguments.d.ts.map +1 -0
  291. package/dist/core/sandbox/bwrap-arguments.js +86 -0
  292. package/dist/core/sandbox/bwrap-arguments.js.map +1 -0
  293. package/dist/core/sandbox/child-entry.d.ts +2 -0
  294. package/dist/core/sandbox/child-entry.d.ts.map +1 -0
  295. package/dist/core/sandbox/child-entry.js +22 -0
  296. package/dist/core/sandbox/child-entry.js.map +1 -0
  297. package/dist/core/sandbox/cli-launch.d.ts +88 -0
  298. package/dist/core/sandbox/cli-launch.d.ts.map +1 -0
  299. package/dist/core/sandbox/cli-launch.js +272 -0
  300. package/dist/core/sandbox/cli-launch.js.map +1 -0
  301. package/dist/core/sandbox/cli-supervisor.d.ts +30 -0
  302. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -0
  303. package/dist/core/sandbox/cli-supervisor.js +123 -0
  304. package/dist/core/sandbox/cli-supervisor.js.map +1 -0
  305. package/dist/core/sandbox/default-hosts.d.ts +28 -0
  306. package/dist/core/sandbox/default-hosts.d.ts.map +1 -0
  307. package/dist/core/sandbox/default-hosts.js +69 -0
  308. package/dist/core/sandbox/default-hosts.js.map +1 -0
  309. package/dist/core/sandbox/full-access.d.ts +28 -0
  310. package/dist/core/sandbox/full-access.d.ts.map +1 -0
  311. package/dist/core/sandbox/full-access.js +63 -0
  312. package/dist/core/sandbox/full-access.js.map +1 -0
  313. package/dist/core/sandbox/git-identity.d.ts +57 -0
  314. package/dist/core/sandbox/git-identity.d.ts.map +1 -0
  315. package/dist/core/sandbox/git-identity.js +78 -0
  316. package/dist/core/sandbox/git-identity.js.map +1 -0
  317. package/dist/core/sandbox/host-approval.d.ts +42 -0
  318. package/dist/core/sandbox/host-approval.d.ts.map +1 -0
  319. package/dist/core/sandbox/host-approval.js +99 -0
  320. package/dist/core/sandbox/host-approval.js.map +1 -0
  321. package/dist/core/sandbox/linux-backend.d.ts +46 -0
  322. package/dist/core/sandbox/linux-backend.d.ts.map +1 -0
  323. package/dist/core/sandbox/linux-backend.js +304 -0
  324. package/dist/core/sandbox/linux-backend.js.map +1 -0
  325. package/dist/core/sandbox/macos-backend.d.ts +28 -0
  326. package/dist/core/sandbox/macos-backend.d.ts.map +1 -0
  327. package/dist/core/sandbox/macos-backend.js +313 -0
  328. package/dist/core/sandbox/macos-backend.js.map +1 -0
  329. package/dist/core/sandbox/network-proxy.d.ts +38 -0
  330. package/dist/core/sandbox/network-proxy.d.ts.map +1 -0
  331. package/dist/core/sandbox/network-proxy.js +153 -0
  332. package/dist/core/sandbox/network-proxy.js.map +1 -0
  333. package/dist/core/sandbox/network-refusal.d.ts +24 -0
  334. package/dist/core/sandbox/network-refusal.d.ts.map +1 -0
  335. package/dist/core/sandbox/network-refusal.js +72 -0
  336. package/dist/core/sandbox/network-refusal.js.map +1 -0
  337. package/dist/core/sandbox/policy.d.ts +40 -0
  338. package/dist/core/sandbox/policy.d.ts.map +1 -0
  339. package/dist/core/sandbox/policy.js +55 -0
  340. package/dist/core/sandbox/policy.js.map +1 -0
  341. package/dist/core/sandbox/profiles.d.ts +25 -0
  342. package/dist/core/sandbox/profiles.d.ts.map +1 -0
  343. package/dist/core/sandbox/profiles.js +23 -0
  344. package/dist/core/sandbox/profiles.js.map +1 -0
  345. package/dist/core/sandbox/rpc/command-client.d.ts +28 -0
  346. package/dist/core/sandbox/rpc/command-client.d.ts.map +1 -0
  347. package/dist/core/sandbox/rpc/command-client.js +86 -0
  348. package/dist/core/sandbox/rpc/command-client.js.map +1 -0
  349. package/dist/core/sandbox/rpc/command-proxy.d.ts +48 -0
  350. package/dist/core/sandbox/rpc/command-proxy.d.ts.map +1 -0
  351. package/dist/core/sandbox/rpc/command-proxy.js +120 -0
  352. package/dist/core/sandbox/rpc/command-proxy.js.map +1 -0
  353. package/dist/core/sandbox/rpc/credential-client.d.ts +20 -0
  354. package/dist/core/sandbox/rpc/credential-client.d.ts.map +1 -0
  355. package/dist/core/sandbox/rpc/credential-client.js +170 -0
  356. package/dist/core/sandbox/rpc/credential-client.js.map +1 -0
  357. package/dist/core/sandbox/rpc/credential-proxy.d.ts +44 -0
  358. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -0
  359. package/dist/core/sandbox/rpc/credential-proxy.js +287 -0
  360. package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -0
  361. package/dist/core/sandbox/rpc/framing.d.ts +35 -0
  362. package/dist/core/sandbox/rpc/framing.d.ts.map +1 -0
  363. package/dist/core/sandbox/rpc/framing.js +114 -0
  364. package/dist/core/sandbox/rpc/framing.js.map +1 -0
  365. package/dist/core/sandbox/rpc/git-credential-helper.d.ts +40 -0
  366. package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +1 -0
  367. package/dist/core/sandbox/rpc/git-credential-helper.js +152 -0
  368. package/dist/core/sandbox/rpc/git-credential-helper.js.map +1 -0
  369. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +48 -0
  370. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +1 -0
  371. package/dist/core/sandbox/rpc/git-credential-proxy.js +142 -0
  372. package/dist/core/sandbox/rpc/git-credential-proxy.js.map +1 -0
  373. package/dist/core/sandbox/supervisor-temp.d.ts +18 -0
  374. package/dist/core/sandbox/supervisor-temp.d.ts.map +1 -0
  375. package/dist/core/sandbox/supervisor-temp.js +21 -0
  376. package/dist/core/sandbox/supervisor-temp.js.map +1 -0
  377. package/dist/core/sandbox/supervisor.d.ts +51 -0
  378. package/dist/core/sandbox/supervisor.d.ts.map +1 -0
  379. package/dist/core/sandbox/supervisor.js +18 -0
  380. package/dist/core/sandbox/supervisor.js.map +1 -0
  381. package/dist/core/sandbox/terminal-handoff.d.ts +45 -0
  382. package/dist/core/sandbox/terminal-handoff.d.ts.map +1 -0
  383. package/dist/core/sandbox/terminal-handoff.js +176 -0
  384. package/dist/core/sandbox/terminal-handoff.js.map +1 -0
  385. package/dist/core/sandbox/terminal-size.d.ts +40 -0
  386. package/dist/core/sandbox/terminal-size.d.ts.map +1 -0
  387. package/dist/core/sandbox/terminal-size.js +105 -0
  388. package/dist/core/sandbox/terminal-size.js.map +1 -0
  389. package/dist/core/sandbox/violations.d.ts +23 -0
  390. package/dist/core/sandbox/violations.d.ts.map +1 -0
  391. package/dist/core/sandbox/violations.js +30 -0
  392. package/dist/core/sandbox/violations.js.map +1 -0
  393. package/dist/core/sdk.d.ts +32 -41
  394. package/dist/core/sdk.d.ts.map +1 -1
  395. package/dist/core/sdk.js +174 -6
  396. package/dist/core/sdk.js.map +1 -1
  397. package/dist/core/session-export.d.ts +4 -0
  398. package/dist/core/session-export.d.ts.map +1 -0
  399. package/dist/core/session-export.js +32 -0
  400. package/dist/core/session-export.js.map +1 -0
  401. package/dist/core/session-lease.d.ts +31 -0
  402. package/dist/core/session-lease.d.ts.map +1 -0
  403. package/dist/core/session-lease.js +138 -0
  404. package/dist/core/session-lease.js.map +1 -0
  405. package/dist/core/session-manager.d.ts +58 -2
  406. package/dist/core/session-manager.d.ts.map +1 -1
  407. package/dist/core/session-manager.js +92 -7
  408. package/dist/core/session-manager.js.map +1 -1
  409. package/dist/core/session-share.d.ts +31 -0
  410. package/dist/core/session-share.d.ts.map +1 -0
  411. package/dist/core/session-share.js +45 -0
  412. package/dist/core/session-share.js.map +1 -0
  413. package/dist/core/settings-diagnostics.d.ts +9 -0
  414. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  415. package/dist/core/settings-diagnostics.js +21 -0
  416. package/dist/core/settings-diagnostics.js.map +1 -0
  417. package/dist/core/settings-manager.d.ts +99 -9
  418. package/dist/core/settings-manager.d.ts.map +1 -1
  419. package/dist/core/settings-manager.js +141 -36
  420. package/dist/core/settings-manager.js.map +1 -1
  421. package/dist/core/skills.d.ts +19 -7
  422. package/dist/core/skills.d.ts.map +1 -1
  423. package/dist/core/skills.js +128 -52
  424. package/dist/core/skills.js.map +1 -1
  425. package/dist/core/slash-commands.d.ts.map +1 -1
  426. package/dist/core/slash-commands.js +6 -2
  427. package/dist/core/slash-commands.js.map +1 -1
  428. package/dist/core/system-prompt.d.ts +2 -2
  429. package/dist/core/system-prompt.d.ts.map +1 -1
  430. package/dist/core/system-prompt.js +59 -68
  431. package/dist/core/system-prompt.js.map +1 -1
  432. package/dist/core/timings.d.ts +1 -1
  433. package/dist/core/timings.d.ts.map +1 -1
  434. package/dist/core/timings.js +3 -2
  435. package/dist/core/timings.js.map +1 -1
  436. package/dist/core/tools/ask-user.d.ts +25 -0
  437. package/dist/core/tools/ask-user.d.ts.map +1 -0
  438. package/dist/core/tools/ask-user.js +49 -0
  439. package/dist/core/tools/ask-user.js.map +1 -0
  440. package/dist/core/tools/bash-command-segments.d.ts +22 -0
  441. package/dist/core/tools/bash-command-segments.d.ts.map +1 -0
  442. package/dist/core/tools/bash-command-segments.js +128 -0
  443. package/dist/core/tools/bash-command-segments.js.map +1 -0
  444. package/dist/core/tools/bash.d.ts +40 -4
  445. package/dist/core/tools/bash.d.ts.map +1 -1
  446. package/dist/core/tools/bash.js +183 -32
  447. package/dist/core/tools/bash.js.map +1 -1
  448. package/dist/core/tools/contract-snapshot.d.ts +58 -0
  449. package/dist/core/tools/contract-snapshot.d.ts.map +1 -0
  450. package/dist/core/tools/contract-snapshot.js +51 -0
  451. package/dist/core/tools/contract-snapshot.js.map +1 -0
  452. package/dist/core/tools/contract.d.ts +185 -0
  453. package/dist/core/tools/contract.d.ts.map +1 -0
  454. package/dist/core/tools/contract.js +50 -0
  455. package/dist/core/tools/contract.js.map +1 -0
  456. package/dist/core/tools/delegate.d.ts +38 -0
  457. package/dist/core/tools/delegate.d.ts.map +1 -0
  458. package/dist/core/tools/delegate.js +90 -0
  459. package/dist/core/tools/delegate.js.map +1 -0
  460. package/dist/core/tools/diagnostics.d.ts +39 -0
  461. package/dist/core/tools/diagnostics.d.ts.map +1 -0
  462. package/dist/core/tools/diagnostics.js +62 -0
  463. package/dist/core/tools/diagnostics.js.map +1 -0
  464. package/dist/core/tools/edit-diff.d.ts +0 -5
  465. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  466. package/dist/core/tools/edit-diff.js +2 -5
  467. package/dist/core/tools/edit-diff.js.map +1 -1
  468. package/dist/core/tools/edit.d.ts +12 -2
  469. package/dist/core/tools/edit.d.ts.map +1 -1
  470. package/dist/core/tools/edit.js +61 -10
  471. package/dist/core/tools/edit.js.map +1 -1
  472. package/dist/core/tools/find.d.ts +2 -2
  473. package/dist/core/tools/find.d.ts.map +1 -1
  474. package/dist/core/tools/find.js +13 -1
  475. package/dist/core/tools/find.js.map +1 -1
  476. package/dist/core/tools/grep.d.ts +4 -2
  477. package/dist/core/tools/grep.d.ts.map +1 -1
  478. package/dist/core/tools/grep.js +47 -10
  479. package/dist/core/tools/grep.js.map +1 -1
  480. package/dist/core/tools/index.d.ts +60 -6
  481. package/dist/core/tools/index.d.ts.map +1 -1
  482. package/dist/core/tools/index.js +148 -2
  483. package/dist/core/tools/index.js.map +1 -1
  484. package/dist/core/tools/ls.d.ts +4 -2
  485. package/dist/core/tools/ls.d.ts.map +1 -1
  486. package/dist/core/tools/ls.js +17 -1
  487. package/dist/core/tools/ls.js.map +1 -1
  488. package/dist/core/tools/lsp.d.ts +62 -0
  489. package/dist/core/tools/lsp.d.ts.map +1 -0
  490. package/dist/core/tools/lsp.js +216 -0
  491. package/dist/core/tools/lsp.js.map +1 -0
  492. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  493. package/dist/core/tools/output-accumulator.js +1 -1
  494. package/dist/core/tools/output-accumulator.js.map +1 -1
  495. package/dist/core/tools/path-permission.d.ts +18 -0
  496. package/dist/core/tools/path-permission.d.ts.map +1 -0
  497. package/dist/core/tools/path-permission.js +28 -0
  498. package/dist/core/tools/path-permission.js.map +1 -0
  499. package/dist/core/tools/plan-present.d.ts +27 -0
  500. package/dist/core/tools/plan-present.d.ts.map +1 -0
  501. package/dist/core/tools/plan-present.js +55 -0
  502. package/dist/core/tools/plan-present.js.map +1 -0
  503. package/dist/core/tools/powershell.d.ts +16 -0
  504. package/dist/core/tools/powershell.d.ts.map +1 -0
  505. package/dist/core/tools/powershell.js +39 -0
  506. package/dist/core/tools/powershell.js.map +1 -0
  507. package/dist/core/tools/read.d.ts +2 -2
  508. package/dist/core/tools/read.d.ts.map +1 -1
  509. package/dist/core/tools/read.js +14 -0
  510. package/dist/core/tools/read.js.map +1 -1
  511. package/dist/core/tools/skill-search.d.ts +32 -0
  512. package/dist/core/tools/skill-search.d.ts.map +1 -0
  513. package/dist/core/tools/skill-search.js +56 -0
  514. package/dist/core/tools/skill-search.js.map +1 -0
  515. package/dist/core/tools/test.d.ts +34 -0
  516. package/dist/core/tools/test.d.ts.map +1 -0
  517. package/dist/core/tools/test.js +75 -0
  518. package/dist/core/tools/test.js.map +1 -0
  519. package/dist/core/tools/todo-write.d.ts +31 -0
  520. package/dist/core/tools/todo-write.d.ts.map +1 -0
  521. package/dist/core/tools/todo-write.js +51 -0
  522. package/dist/core/tools/todo-write.js.map +1 -0
  523. package/dist/core/tools/tool-definition-wrapper.d.ts +13 -2
  524. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  525. package/dist/core/tools/tool-definition-wrapper.js +11 -1
  526. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  527. package/dist/core/tools/tool-schema.d.ts +22 -0
  528. package/dist/core/tools/tool-schema.d.ts.map +1 -0
  529. package/dist/core/tools/tool-schema.js +45 -0
  530. package/dist/core/tools/tool-schema.js.map +1 -0
  531. package/dist/core/tools/web-fetch.d.ts +26 -0
  532. package/dist/core/tools/web-fetch.d.ts.map +1 -0
  533. package/dist/core/tools/web-fetch.js +62 -0
  534. package/dist/core/tools/web-fetch.js.map +1 -0
  535. package/dist/core/tools/web-search-exa.d.ts +33 -0
  536. package/dist/core/tools/web-search-exa.d.ts.map +1 -0
  537. package/dist/core/tools/web-search-exa.js +128 -0
  538. package/dist/core/tools/web-search-exa.js.map +1 -0
  539. package/dist/core/tools/web-search.d.ts +26 -0
  540. package/dist/core/tools/web-search.d.ts.map +1 -0
  541. package/dist/core/tools/web-search.js +53 -0
  542. package/dist/core/tools/web-search.js.map +1 -0
  543. package/dist/core/tools/write.d.ts +16 -2
  544. package/dist/core/tools/write.d.ts.map +1 -1
  545. package/dist/core/tools/write.js +48 -2
  546. package/dist/core/tools/write.js.map +1 -1
  547. package/dist/core/trust-manager.d.ts.map +1 -1
  548. package/dist/core/trust-manager.js +2 -1
  549. package/dist/core/trust-manager.js.map +1 -1
  550. package/dist/core/usage-performance-store.d.ts +56 -0
  551. package/dist/core/usage-performance-store.d.ts.map +1 -0
  552. package/dist/core/usage-performance-store.js +85 -0
  553. package/dist/core/usage-performance-store.js.map +1 -0
  554. package/dist/core/web-search-provider.d.ts +64 -0
  555. package/dist/core/web-search-provider.d.ts.map +1 -0
  556. package/dist/core/web-search-provider.js +149 -0
  557. package/dist/core/web-search-provider.js.map +1 -0
  558. package/dist/extensions/llama/index.d.ts.map +1 -1
  559. package/dist/extensions/llama/index.js +2 -0
  560. package/dist/extensions/llama/index.js.map +1 -1
  561. package/dist/extensions/llama/provider.d.ts.map +1 -1
  562. package/dist/extensions/llama/provider.js +6 -2
  563. package/dist/extensions/llama/provider.js.map +1 -1
  564. package/dist/index.d.ts +9 -6
  565. package/dist/index.d.ts.map +1 -1
  566. package/dist/index.js +7 -4
  567. package/dist/index.js.map +1 -1
  568. package/dist/main.d.ts +5 -0
  569. package/dist/main.d.ts.map +1 -1
  570. package/dist/main.js +87 -22
  571. package/dist/main.js.map +1 -1
  572. package/dist/migrations.d.ts.map +1 -1
  573. package/dist/migrations.js +6 -5
  574. package/dist/migrations.js.map +1 -1
  575. package/dist/modes/interactive/components/assistant-message.d.ts +29 -0
  576. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  577. package/dist/modes/interactive/components/assistant-message.js +182 -23
  578. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  579. package/dist/modes/interactive/components/bash-execution.d.ts +7 -0
  580. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  581. package/dist/modes/interactive/components/bash-execution.js +20 -0
  582. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  583. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  584. package/dist/modes/interactive/components/config-selector.js +9 -6
  585. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  586. package/dist/modes/interactive/components/custom-editor.d.ts +72 -1
  587. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  588. package/dist/modes/interactive/components/custom-editor.js +338 -1
  589. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  590. package/dist/modes/interactive/components/dynamic-border.d.ts +7 -1
  591. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  592. package/dist/modes/interactive/components/dynamic-border.js +11 -2
  593. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  594. package/dist/modes/interactive/components/extension-selector.d.ts +4 -0
  595. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  596. package/dist/modes/interactive/components/extension-selector.js +24 -6
  597. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  598. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -4
  599. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  600. package/dist/modes/interactive/components/first-time-setup.js +17 -41
  601. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  602. package/dist/modes/interactive/components/first-use-hints.d.ts +8 -0
  603. package/dist/modes/interactive/components/first-use-hints.d.ts.map +1 -0
  604. package/dist/modes/interactive/components/first-use-hints.js +22 -0
  605. package/dist/modes/interactive/components/first-use-hints.js.map +1 -0
  606. package/dist/modes/interactive/components/footer.d.ts +31 -1
  607. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  608. package/dist/modes/interactive/components/footer.js +205 -24
  609. package/dist/modes/interactive/components/footer.js.map +1 -1
  610. package/dist/modes/interactive/components/index.d.ts +2 -0
  611. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  612. package/dist/modes/interactive/components/index.js +2 -0
  613. package/dist/modes/interactive/components/index.js.map +1 -1
  614. package/dist/modes/interactive/components/model-selector.d.ts +31 -9
  615. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  616. package/dist/modes/interactive/components/model-selector.js +236 -96
  617. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  618. package/dist/modes/interactive/components/settings-selector.d.ts +25 -5
  619. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  620. package/dist/modes/interactive/components/settings-selector.js +226 -58
  621. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  622. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  623. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  624. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  625. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  626. package/dist/modes/interactive/components/splash-header.d.ts +66 -0
  627. package/dist/modes/interactive/components/splash-header.d.ts.map +1 -0
  628. package/dist/modes/interactive/components/splash-header.js +180 -0
  629. package/dist/modes/interactive/components/splash-header.js.map +1 -0
  630. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  631. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  632. package/dist/modes/interactive/components/thinking-selector.js +74 -17
  633. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  634. package/dist/modes/interactive/components/tool-execution.d.ts +28 -1
  635. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  636. package/dist/modes/interactive/components/tool-execution.js +144 -35
  637. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  638. package/dist/modes/interactive/components/tool-panel.d.ts +30 -0
  639. package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -0
  640. package/dist/modes/interactive/components/tool-panel.js +130 -0
  641. package/dist/modes/interactive/components/tool-panel.js.map +1 -0
  642. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  643. package/dist/modes/interactive/external-editor.js +60 -10
  644. package/dist/modes/interactive/external-editor.js.map +1 -1
  645. package/dist/modes/interactive/interactive-mode.d.ts +90 -5
  646. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  647. package/dist/modes/interactive/interactive-mode.js +729 -206
  648. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  649. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  650. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  651. package/dist/modes/interactive/model-catalog-refresh.js +34 -0
  652. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  653. package/dist/modes/interactive/theme/apex.json +93 -0
  654. package/dist/modes/interactive/theme/dark.json +2 -0
  655. package/dist/modes/interactive/theme/light.json +3 -1
  656. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  657. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  658. package/dist/modes/interactive/theme/theme-controller.js +25 -11
  659. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  660. package/dist/modes/interactive/theme/theme-schema.json +11 -3
  661. package/dist/modes/interactive/theme/theme.d.ts +14 -3
  662. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  663. package/dist/modes/interactive/theme/theme.js +65 -10
  664. package/dist/modes/interactive/theme/theme.js.map +1 -1
  665. package/dist/modes/json-event.d.ts +18 -12
  666. package/dist/modes/json-event.d.ts.map +1 -1
  667. package/dist/modes/json-event.js +22 -5
  668. package/dist/modes/json-event.js.map +1 -1
  669. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  670. package/dist/modes/rpc/rpc-mode.js +2 -1
  671. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  672. package/dist/package-manager-cli.d.ts +1 -0
  673. package/dist/package-manager-cli.d.ts.map +1 -1
  674. package/dist/package-manager-cli.js +210 -19
  675. package/dist/package-manager-cli.js.map +1 -1
  676. package/dist/rpc-entry.d.ts.map +1 -1
  677. package/dist/rpc-entry.js +2 -1
  678. package/dist/rpc-entry.js.map +1 -1
  679. package/dist/testing/replay/metrics.d.ts +24 -0
  680. package/dist/testing/replay/metrics.d.ts.map +1 -0
  681. package/dist/testing/replay/metrics.js +35 -0
  682. package/dist/testing/replay/metrics.js.map +1 -0
  683. package/dist/testing/replay/recorded-provider.d.ts +10 -0
  684. package/dist/testing/replay/recorded-provider.d.ts.map +1 -0
  685. package/dist/testing/replay/recorded-provider.js +108 -0
  686. package/dist/testing/replay/recorded-provider.js.map +1 -0
  687. package/dist/testing/replay/runner.d.ts +56 -0
  688. package/dist/testing/replay/runner.d.ts.map +1 -0
  689. package/dist/testing/replay/runner.js +406 -0
  690. package/dist/testing/replay/runner.js.map +1 -0
  691. package/dist/themes/apex-logo.d.ts +41 -0
  692. package/dist/themes/apex-logo.d.ts.map +1 -0
  693. package/dist/themes/apex-logo.js +35 -0
  694. package/dist/themes/apex-logo.js.map +1 -0
  695. package/dist/utils/apex-code-user-agent.d.ts +2 -0
  696. package/dist/utils/apex-code-user-agent.d.ts.map +1 -0
  697. package/dist/utils/apex-code-user-agent.js +5 -0
  698. package/dist/utils/apex-code-user-agent.js.map +1 -0
  699. package/dist/utils/changelog.d.ts +7 -2
  700. package/dist/utils/changelog.d.ts.map +1 -1
  701. package/dist/utils/changelog.js +82 -13
  702. package/dist/utils/changelog.js.map +1 -1
  703. package/dist/utils/clipboard-image.d.ts.map +1 -1
  704. package/dist/utils/clipboard-image.js +1 -1
  705. package/dist/utils/clipboard-image.js.map +1 -1
  706. package/dist/utils/frontmatter.d.ts.map +1 -1
  707. package/dist/utils/frontmatter.js +2 -1
  708. package/dist/utils/frontmatter.js.map +1 -1
  709. package/dist/utils/management-http.d.ts +5 -3
  710. package/dist/utils/management-http.d.ts.map +1 -1
  711. package/dist/utils/management-http.js +14 -10
  712. package/dist/utils/management-http.js.map +1 -1
  713. package/dist/utils/shell.d.ts +3 -0
  714. package/dist/utils/shell.d.ts.map +1 -1
  715. package/dist/utils/shell.js +17 -5
  716. package/dist/utils/shell.js.map +1 -1
  717. package/dist/utils/syntax-highlight.d.ts +1 -0
  718. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  719. package/dist/utils/syntax-highlight.js +60 -1
  720. package/dist/utils/syntax-highlight.js.map +1 -1
  721. package/dist/utils/text.d.ts +8 -0
  722. package/dist/utils/text.d.ts.map +1 -0
  723. package/dist/utils/text.js +9 -0
  724. package/dist/utils/text.js.map +1 -0
  725. package/dist/utils/tools-manager.d.ts +70 -2
  726. package/dist/utils/tools-manager.d.ts.map +1 -1
  727. package/dist/utils/tools-manager.js +280 -124
  728. package/dist/utils/tools-manager.js.map +1 -1
  729. package/dist/utils/version-check.d.ts +7 -5
  730. package/dist/utils/version-check.d.ts.map +1 -1
  731. package/dist/utils/version-check.js +27 -14
  732. package/dist/utils/version-check.js.map +1 -1
  733. package/dist/utils/windows-self-update.d.ts.map +1 -1
  734. package/dist/utils/windows-self-update.js +1 -1
  735. package/dist/utils/windows-self-update.js.map +1 -1
  736. package/docs/compaction.md +41 -24
  737. package/docs/containerization.md +24 -21
  738. package/docs/custom-provider.md +15 -12
  739. package/docs/development.md +11 -11
  740. package/docs/environment-variables.md +37 -77
  741. package/docs/extensions.md +68 -54
  742. package/docs/index.md +12 -24
  743. package/docs/json.md +18 -11
  744. package/docs/keybindings.md +18 -12
  745. package/docs/llama-cpp.md +9 -7
  746. package/docs/models.md +17 -11
  747. package/docs/packages.md +47 -43
  748. package/docs/prompt-templates.md +3 -3
  749. package/docs/providers.md +10 -10
  750. package/docs/quickstart.md +49 -32
  751. package/docs/rpc.md +35 -18
  752. package/docs/sdk.md +20 -6
  753. package/docs/security.md +18 -18
  754. package/docs/session-format.md +9 -9
  755. package/docs/sessions.md +13 -13
  756. package/docs/settings.md +83 -16
  757. package/docs/shell-aliases.md +1 -1
  758. package/docs/skills.md +66 -15
  759. package/docs/terminal-setup.md +44 -19
  760. package/docs/termux.md +4 -4
  761. package/docs/themes.md +32 -11
  762. package/docs/tmux.md +4 -4
  763. package/docs/tui.md +5 -5
  764. package/docs/usage.md +61 -53
  765. package/docs/windows.md +24 -2
  766. package/examples/extensions/border-status-editor.ts +1 -1
  767. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  768. package/examples/extensions/custom-provider-anthropic/package.json +2 -2
  769. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  770. package/examples/extensions/git-checkpoint.ts +24 -36
  771. package/examples/extensions/gondolin/package-lock.json +2 -2
  772. package/examples/extensions/gondolin/package.json +1 -1
  773. package/examples/extensions/notify.ts +3 -1
  774. package/examples/extensions/sandbox/package-lock.json +2 -2
  775. package/examples/extensions/sandbox/package.json +1 -1
  776. package/examples/extensions/subagent/README.md +3 -1
  777. package/examples/extensions/subagent/agents.ts +40 -9
  778. package/examples/extensions/subagent/index.ts +27 -4
  779. package/examples/extensions/titlebar-spinner.ts +1 -1
  780. package/examples/extensions/with-deps/package-lock.json +2 -2
  781. package/examples/extensions/with-deps/package.json +1 -1
  782. package/examples/rpc-extension-ui.ts +1 -1
  783. package/examples/sdk/06-extensions.ts +1 -1
  784. package/examples/sdk/README.md +1 -1
  785. package/npm-shrinkwrap.json +435 -452
  786. package/package.json +17 -18
  787. package/dist/core/telemetry.d.ts +0 -3
  788. package/dist/core/telemetry.d.ts.map +0 -1
  789. package/dist/core/telemetry.js +0 -9
  790. package/dist/core/telemetry.js.map +0 -1
  791. package/dist/server/create-harness.d.ts +0 -26
  792. package/dist/server/create-harness.d.ts.map +0 -1
  793. package/dist/server/create-harness.js +0 -104
  794. package/dist/server/create-harness.js.map +0 -1
  795. package/dist/utils/pi-user-agent.d.ts +0 -2
  796. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  797. package/dist/utils/pi-user-agent.js +0 -5
  798. package/dist/utils/pi-user-agent.js.map +0 -1
@@ -14,17 +14,22 @@ import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebu
14
14
  import { parseSkillBlock } from "../../core/agent-session.js";
15
15
  import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
16
16
  import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
17
+ import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "../../core/defaults.js";
18
+ import { getApexEnvironment } from "../../core/environment.js";
17
19
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
18
20
  import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
19
21
  import { KeybindingsManager } from "../../core/keybindings.js";
20
22
  import { createCompactionSummaryMessage } from "../../core/messages.js";
21
23
  import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScopeFromModels, } from "../../core/model-resolver.js";
22
24
  import { CredentialSynchronizationError } from "../../core/model-runtime.js";
25
+ import { aggregateUsagePerformance } from "../../core/observability/aggregate.js";
23
26
  import { DefaultPackageManager } from "../../core/package-manager.js";
27
+ import { observeTerminalHandoff, TERMINAL_HANDOFF_PATH_VARIABLE } from "../../core/sandbox/terminal-handoff.js";
24
28
  import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
25
29
  import { SessionManager, sessionEntryToContextMessages } from "../../core/session-manager.js";
30
+ import { formatShareUnavailableMessage, publishSessionShare } from "../../core/session-share.js";
31
+ import { slugifySkillCommandName } from "../../core/skills.js";
26
32
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
27
- import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
28
33
  import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
29
34
  import { getUsageCostBreakdown } from "../../core/usage-totals.js";
30
35
  import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.js";
@@ -33,10 +38,10 @@ import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipb
33
38
  import { parseGitUrl } from "../../utils/git.js";
34
39
  import { openBrowser } from "../../utils/open-browser.js";
35
40
  import { getCwdRelativePath } from "../../utils/paths.js";
36
- import { getPiUserAgent } from "../../utils/pi-user-agent.js";
37
41
  import { killTrackedDetachedChildren } from "../../utils/shell.js";
42
+ import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
38
43
  import { ensureTool } from "../../utils/tools-manager.js";
39
- import { checkForNewPiVersion } from "../../utils/version-check.js";
44
+ import { checkForNewApexCodeVersion } from "../../utils/version-check.js";
40
45
  import { ArminComponent } from "./components/armin.js";
41
46
  import { AssistantMessageComponent } from "./components/assistant-message.js";
42
47
  import { BashExecutionComponent } from "./components/bash-execution.js";
@@ -52,6 +57,7 @@ import { EarendilAnnouncementComponent } from "./components/earendil-announcemen
52
57
  import { ExtensionEditorComponent } from "./components/extension-editor.js";
53
58
  import { ExtensionInputComponent } from "./components/extension-input.js";
54
59
  import { ExtensionSelectorComponent } from "./components/extension-selector.js";
60
+ import { FirstUseHints } from "./components/first-use-hints.js";
55
61
  import { FooterComponent, formatTokens } from "./components/footer.js";
56
62
  import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
57
63
  import { LoginDialogComponent } from "./components/login-dialog.js";
@@ -60,15 +66,18 @@ import { ModelSelectorComponent } from "./components/model-selector.js";
60
66
  import { formatAuthSelectorProviderType, OAuthSelectorComponent, } from "./components/oauth-selector.js";
61
67
  import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js";
62
68
  import { SessionSelectorComponent } from "./components/session-selector.js";
63
- import { SettingsSelectorComponent } from "./components/settings-selector.js";
69
+ import { PERMISSION_MODE_OVERRIDE_HINTS, SettingsSelectorComponent } from "./components/settings-selector.js";
64
70
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
71
+ import { ApexSplashHeader } from "./components/splash-header.js";
65
72
  import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
73
+ import { ThinkingSelectorComponent } from "./components/thinking-selector.js";
66
74
  import { ToolExecutionComponent } from "./components/tool-execution.js";
67
75
  import { TreeSelectorComponent } from "./components/tree-selector.js";
68
76
  import { TrustSelectorComponent } from "./components/trust-selector.js";
69
77
  import { UserMessageComponent } from "./components/user-message.js";
70
78
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
71
79
  import { editInExternalEditor } from "./external-editor.js";
80
+ import { refreshModelCatalogs } from "./model-catalog-refresh.js";
72
81
  import { getModelSearchText } from "./model-search.js";
73
82
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
74
83
  import { InteractiveThemeController } from "./theme/theme-controller.js";
@@ -90,6 +99,9 @@ class ExpandableText extends Text {
90
99
  function isCustomSessionEntry(item) {
91
100
  return "type" in item && item.type === "custom";
92
101
  }
102
+ function isCompactionCostNotice(item) {
103
+ return "type" in item && item.type === "compaction_cost";
104
+ }
93
105
  const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
94
106
  function isDeadTerminalError(error) {
95
107
  if (!error || typeof error !== "object" || !("code" in error)) {
@@ -129,6 +141,11 @@ export function formatResumeCommand(sessionManager) {
129
141
  function hasDefaultModelProvider(providerId) {
130
142
  return providerId in defaultModelPerProvider;
131
143
  }
144
+ function llamaCppPostLoginGuidance(actionLabel, loadedModelCount) {
145
+ return loadedModelCount === 0
146
+ ? `${actionLabel}. No llama.cpp models are loaded. Use /llama to load a model, then /model to select it.`
147
+ : `${actionLabel}. Use /model to select a loaded llama.cpp model, or /llama to manage models.`;
148
+ }
132
149
  const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
133
150
  function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
134
151
  const filtered = fuzzyFilter(items, prefix, getSearchText);
@@ -169,9 +186,21 @@ function formatLoginProviderCompletionDescription(provider) {
169
186
  export function createInteractiveTui(options) {
170
187
  const terminal = options.terminal ?? new ProcessTerminal();
171
188
  if (options.tuiMode === "fullscreen") {
189
+ const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
172
190
  return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
191
+ searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
192
+ searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
173
193
  openUrl: openBrowser,
174
194
  onRightClickPaste: options.onRightClickPaste,
195
+ copySelection: async (text) => {
196
+ try {
197
+ await copyToClipboard(text);
198
+ return true;
199
+ }
200
+ catch {
201
+ return false;
202
+ }
203
+ },
175
204
  });
176
205
  }
177
206
  return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
@@ -209,6 +238,8 @@ export function createInteractiveTuiReference(getTui) {
209
238
  }
210
239
  export class InteractiveMode {
211
240
  runtimeHost;
241
+ /** Settled once the most recent /settings permission-mode write has hit disk. */
242
+ pendingPermissionModeWrite;
212
243
  renderer;
213
244
  ui;
214
245
  mainScreenRenderState;
@@ -231,6 +262,14 @@ export class InteractiveMode {
231
262
  footer;
232
263
  footerContainer;
233
264
  footerDataProvider;
265
+ /**
266
+ * Whether the startup screen is showing its counted inventory line.
267
+ *
268
+ * Warnings are only allowed to collapse into that count when the count is
269
+ * actually on screen. Under a quiet startup there is no splash header, so
270
+ * collapsing them would hide conflicts entirely rather than summarise them.
271
+ */
272
+ startupInventoryVisible = false;
234
273
  // Stored so the same manager can be injected into custom editors, selectors, and extension UI.
235
274
  keybindings;
236
275
  version;
@@ -253,6 +292,7 @@ export class InteractiveMode {
253
292
  // Status line tracking (for mutating immediately-sequential status updates)
254
293
  lastStatusSpacer = undefined;
255
294
  lastStatusText = undefined;
295
+ managedToolStatusStarted = false;
256
296
  // Streaming message tracking
257
297
  streamingComponent = undefined;
258
298
  streamingMessage = undefined;
@@ -274,8 +314,14 @@ export class InteractiveMode {
274
314
  signalCleanupHandlers = [];
275
315
  // Track if editor is in bash mode (text starts with !)
276
316
  isBashMode = false;
317
+ firstUseHints;
277
318
  // Track current bash execution component
278
319
  bashComponent = undefined;
320
+ // Reentrancy guard: session.isBashRunning only turns true inside
321
+ // executeBash, so a second `!cmd` submitted during the extension
322
+ // emitUserBash await would otherwise overwrite the in-flight component
323
+ // and orphan the first command's output callback.
324
+ isHandlingBashCommand = false;
279
325
  // Track pending bash components (shown in pending area, moved to chat on submit)
280
326
  pendingBashComponents = [];
281
327
  // Auto-compaction state
@@ -323,6 +369,14 @@ export class InteractiveMode {
323
369
  get settingsManager() {
324
370
  return this.session.settingsManager;
325
371
  }
372
+ offerFirstUseHint(id) {
373
+ this.firstUseHints ??= new FirstUseHints(this.settingsManager.getFirstUseHints());
374
+ const hint = this.firstUseHints.offer(id);
375
+ if (!hint)
376
+ return;
377
+ this.settingsManager.setFirstUseHints(this.firstUseHints.getSeen());
378
+ this.chatContainer.addChild(new Text(theme.fg("dim", hint), this.outputPad, 0));
379
+ }
326
380
  constructor(runtimeHost, options = {}) {
327
381
  this.runtimeHost = runtimeHost;
328
382
  const tuiMode = options.tuiMode ?? this.settingsManager.getTuiMode();
@@ -333,6 +387,7 @@ export class InteractiveMode {
333
387
  });
334
388
  this.runtimeHost.setRebindSession(async () => {
335
389
  await this.rebindCurrentSession({ renderBeforeBind: true });
390
+ await this.themeController.applyFromSettings();
336
391
  });
337
392
  this.version = VERSION;
338
393
  this.renderer = createInteractiveTui({
@@ -361,12 +416,20 @@ export class InteractiveMode {
361
416
  this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
362
417
  paddingX: editorPaddingX,
363
418
  autocompleteMaxVisible,
419
+ promptPrefix: "› ",
420
+ promptColor: (text) => theme.fg("accent", text),
421
+ placeholder: "Ask anything",
422
+ placeholderColor: (text) => theme.fg("dim", text),
423
+ commandColor: (text) => theme.fg("accent", text),
424
+ surfaceColor: (text) => theme.bg("userMessageBg", text),
425
+ autocompleteRule: (width) => theme.fg("borderMuted", (this.settingsManager.getSymbolPreset() === "ascii" ? "-" : "┄").repeat(Math.max(1, width))),
426
+ autocompleteFooter: () => this.buildAutocompleteFooter(),
364
427
  });
365
428
  this.editor = this.defaultEditor;
366
429
  this.editorContainer = new Container();
367
430
  this.editorContainer.addChild(this.editor);
368
431
  this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
369
- this.footer = new FooterComponent(this.session, this.footerDataProvider);
432
+ this.footer = new FooterComponent(this.session, this.footerDataProvider, this.settingsManager);
370
433
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
371
434
  this.footerContainer = new Container();
372
435
  this.footerContainer.addChild(this.footer);
@@ -375,7 +438,12 @@ export class InteractiveMode {
375
438
  this.outputPad = this.settingsManager.getOutputPad();
376
439
  // Register themes from resource loader and initialize
377
440
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
378
- this.themeController = new InteractiveThemeController(this.ui, this.settingsManager, (message) => this.showError(message), () => this.updateEditorBorderColor());
441
+ this.themeController = new InteractiveThemeController(this.ui, {
442
+ getSettingsManager: () => this.settingsManager,
443
+ showError: (message) => this.showError(message),
444
+ onChanged: () => this.updateEditorBorderColor(),
445
+ initialThemeSetting: options.initialThemeSetting,
446
+ });
379
447
  }
380
448
  getAutocompleteSourceTag(sourceInfo) {
381
449
  if (!sourceInfo) {
@@ -416,8 +484,21 @@ export class InteractiveMode {
416
484
  path: command.sourceInfo.path,
417
485
  }));
418
486
  }
419
- createBaseAutocompleteProvider() {
420
- // Define commands for autocomplete
487
+ /**
488
+ * Every *registered* command: builtin, prompt template, extension, and skill.
489
+ *
490
+ * One function rather than two because `/help` renders this list and autocomplete
491
+ * completes it. Built in two places they would disagree the first time either grew a
492
+ * source, and the disagreement would show up as a command a user can complete but not
493
+ * find, or find but not run.
494
+ *
495
+ * A few commands are dispatched without being registered -- `/debug`, and the
496
+ * `/arminsayshi` and `/dementedelves` easter eggs. They are absent here on purpose,
497
+ * which is also why autocomplete has never offered them. "Registered" is the boundary
498
+ * both surfaces share; listing them in `/help` would advertise what completion cannot
499
+ * complete.
500
+ */
501
+ collectSessionCommands() {
421
502
  const slashCommands = BUILTIN_SLASH_COMMANDS.map((command) => ({
422
503
  name: command.name,
423
504
  description: command.description,
@@ -445,6 +526,15 @@ export class InteractiveMode {
445
526
  }));
446
527
  };
447
528
  }
529
+ const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
530
+ if (thinkingCommand) {
531
+ thinkingCommand.getArgumentCompletions = (prefix) => {
532
+ return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
533
+ value: level,
534
+ label: level,
535
+ }));
536
+ };
537
+ }
448
538
  const loginCommand = slashCommands.find((command) => command.name === "login");
449
539
  if (loginCommand) {
450
540
  loginCommand.getArgumentCompletions = (prefix) => {
@@ -477,7 +567,7 @@ export class InteractiveMode {
477
567
  const skillCommandList = [];
478
568
  if (this.settingsManager.getEnableSkillCommands()) {
479
569
  for (const skill of this.session.resourceLoader.getSkills().skills) {
480
- const commandName = `skill:${skill.name}`;
570
+ const commandName = `skill:${slugifySkillCommandName(skill.name)}`;
481
571
  this.skillCommands.set(commandName, skill.filePath);
482
572
  skillCommandList.push({
483
573
  name: commandName,
@@ -485,7 +575,10 @@ export class InteractiveMode {
485
575
  });
486
576
  }
487
577
  }
488
- return new CombinedAutocompleteProvider([...slashCommands, ...templateCommands, ...extensionCommands, ...skillCommandList], this.sessionManager.getCwd(), this.fdPath);
578
+ return [...slashCommands, ...templateCommands, ...extensionCommands, ...skillCommandList];
579
+ }
580
+ createBaseAutocompleteProvider() {
581
+ return new CombinedAutocompleteProvider(this.collectSessionCommands(), this.sessionManager.getCwd(), this.fdPath);
489
582
  }
490
583
  setupAutocompleteProvider() {
491
584
  let provider = this.createBaseAutocompleteProvider();
@@ -516,7 +609,7 @@ export class InteractiveMode {
516
609
  }
517
610
  this.chatContainer.addChild(new DynamicBorder());
518
611
  if (this.settingsManager.getCollapseChangelog()) {
519
- const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
612
+ const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?)\]?/);
520
613
  const latestVersion = versionMatch ? versionMatch[1] : this.version;
521
614
  const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
522
615
  this.chatContainer.addChild(new Text(condensedText, 1, 0));
@@ -538,14 +631,14 @@ export class InteractiveMode {
538
631
  tui.setLayoutRoot(this.fullscreenLayoutRoot);
539
632
  }
540
633
  }
541
- stopInteractiveTui() {
542
- if (this.renderer.mode === "fullscreen") {
634
+ stopInteractiveTui(fullscreenExitOutput) {
635
+ if (this.renderer.mode === "fullscreen" && fullscreenExitOutput === "transcript") {
543
636
  while (this.renderer.hasOverlayEntries)
544
637
  this.renderer.hideOverlay();
545
638
  this.switchTuiMode("regular", false, false);
546
639
  this.renderer.renderNow();
547
640
  }
548
- this.ui.stop();
641
+ this.ui.stop({ preserveScreen: this.renderer.mode === "fullscreen" });
549
642
  }
550
643
  switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
551
644
  const previousUi = this.renderer;
@@ -602,10 +695,9 @@ export class InteractiveMode {
602
695
  this.registerSignalHandlers();
603
696
  // Load changelog (only show new entries, skip for resumed sessions)
604
697
  this.changelogMarkdown = this.getChangelogForDisplay();
605
- // Ensure fd and rg are available (downloads if missing, adds to PATH via getBinDir)
606
- // Both are needed: fd for autocomplete, rg for grep tool and bash commands
607
- const [fdPath] = await Promise.all([ensureTool("fd"), ensureTool("rg")]);
608
- this.fdPath = fdPath;
698
+ // Resolve the effective mode before mounting or focusing components. Both
699
+ // operations can schedule a frame even before the renderer is started.
700
+ await this.refreshFooterPermissionMode();
609
701
  if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
610
702
  const modelList = this.session.scopedModels
611
703
  .map((sm) => {
@@ -649,18 +741,23 @@ export class InteractiveMode {
649
741
  this.widgetContainerBelow,
650
742
  this.footerContainer,
651
743
  ]);
744
+ // Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
745
+ this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
746
+ this.defaultEditor.onCtrlD = () => this.handleCtrlD();
747
+ this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
652
748
  this.ui.setFocus(this.editor);
653
- this.setupKeyHandlers();
654
- this.setupEditorSubmitHandler();
655
749
  // Start the UI before initializing extensions so session_start handlers can use interactive dialogs
656
750
  this.ui.start();
657
751
  this.isInitialized = true;
658
752
  await this.themeController.applyFromSettings();
659
- // Add header with keybindings from config (unless silenced)
753
+ // Brand splash: mark on the left, runtime metadata on the right. The model
754
+ // and cwd are read through live getters, so they fill in once the session
755
+ // binds in rebindCurrentSession below.
660
756
  if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
661
- const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
662
757
  // Build startup instructions using keybinding hint helpers
663
758
  const hint = (keybinding, description) => keyHint(keybinding, description);
759
+ // The full cheatsheet is verbose-only. The default screen leads with the
760
+ // mark and the runtime facts that actually differ between launches.
664
761
  const expandedInstructions = [
665
762
  hint("app.interrupt", "to interrupt"),
666
763
  hint("app.clear", "to clear"),
@@ -682,27 +779,45 @@ export class InteractiveMode {
682
779
  hint("app.clipboard.pasteImage", "to paste image (with text fallback)"),
683
780
  rawKeyHint("drop files", "to attach"),
684
781
  ].join("\n");
685
- const compactInstructions = [
686
- hint("app.interrupt", "interrupt"),
687
- rawKeyHint(`${keyText("app.clear")}/${keyText("app.exit")}`, "clear/exit"),
688
- rawKeyHint("/", "commands"),
689
- rawKeyHint("!", "bash"),
690
- hint("app.tools.expand", "more"),
691
- ].join(theme.fg("muted", " · "));
692
- const compactOnboarding = theme.fg("dim", `Press ${keyText("app.tools.expand")} to show full startup help and loaded resources.`);
693
- const onboarding = theme.fg("dim", `Pi can explain its own features and look up its docs. Ask it how to use or extend Pi.`);
694
- this.builtInHeader = new ExpandableText(() => `${logo}\n${compactInstructions}\n${compactOnboarding}\n\n${onboarding}`, () => `${logo}\n${expandedInstructions}\n\n${onboarding}`, this.getStartupExpansionState(), 1, 0);
782
+ this.builtInHeader = new ApexSplashHeader(this.version, () => this.session.state.model?.id, () => this.session.sessionManager.getCwd(), this.options.verbose ? expandedInstructions : undefined, {
783
+ topPadding: true,
784
+ getSymbolPreset: () => this.settingsManager.getSymbolPreset(),
785
+ getExtraMetadata: () => {
786
+ // Which branch the session is rooted on differs between launches
787
+ // and is not derivable from the prompt, so it belongs here. The
788
+ // tray carries it too, but drops it first when space is tight.
789
+ const branch = this.footerDataProvider.getGitBranch();
790
+ return branch ? [{ label: "branch", value: branch }] : [];
791
+ },
792
+ getInventory: () => this.buildStartupInventory(),
793
+ inventoryHint: "/resources",
794
+ getShortcuts: () => this.buildStartupShortcuts(),
795
+ getHint: () => "Apex Code can explain its own features and look up its docs.",
796
+ });
695
797
  // Setup UI layout
696
- this.headerContainer.addChild(new Spacer(1));
798
+ this.startupInventoryVisible = true;
697
799
  this.headerContainer.addChild(this.builtInHeader);
698
800
  this.headerContainer.addChild(new Spacer(1));
699
801
  }
700
802
  else {
701
803
  // Minimal header when silenced
804
+ this.startupInventoryVisible = false;
702
805
  this.builtInHeader = new Text("", 0, 0);
703
806
  this.headerContainer.addChild(this.builtInHeader);
704
807
  }
705
808
  this.ui.requestRender();
809
+ // Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
810
+ // so slow downloads do not make startup appear frozen.
811
+ // Both are needed: fd for autocomplete, rg for grep tool and bash commands.
812
+ const [fdPath] = await Promise.all([
813
+ ensureTool("fd", (status) => this.showManagedToolStatus(status)),
814
+ ensureTool("rg", (status) => this.showManagedToolStatus(status)),
815
+ ]);
816
+ this.fdPath = fdPath;
817
+ // Enable the remaining input handlers only after managed-tool setup completes.
818
+ this.setupKeyHandlers();
819
+ this.setupEditorSubmitHandler();
820
+ this.ui.requestRender();
706
821
  // Initialize extensions first so resources are shown before messages
707
822
  await this.rebindCurrentSession();
708
823
  // Render initial messages AFTER showing loaded resources
@@ -719,6 +834,14 @@ export class InteractiveMode {
719
834
  });
720
835
  // Initialize available provider count for footer display
721
836
  await this.updateAvailableProviderCount();
837
+ // Flush the completed startup state before loading the remaining syntax grammars.
838
+ this.ui.renderNow();
839
+ void loadAllHighlightLanguages().then(() => {
840
+ if (!this.isInitialized)
841
+ return;
842
+ this.ui.invalidate();
843
+ this.ui.requestRender();
844
+ });
722
845
  }
723
846
  /**
724
847
  * Update terminal title with session name and cwd.
@@ -739,17 +862,16 @@ export class InteractiveMode {
739
862
  */
740
863
  async run() {
741
864
  await this.init();
742
- if (!process.env.PI_OFFLINE) {
865
+ if (!getApexEnvironment("APEX_CODE_OFFLINE")) {
743
866
  const controller = new AbortController();
744
867
  const timeout = setTimeout(() => controller.abort(), 15_000);
745
- void this.session.modelRuntime
746
- .refresh({ signal: controller.signal })
868
+ void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
747
869
  .then(() => this.updateAvailableProviderCount())
748
870
  .catch(() => { })
749
871
  .finally(() => clearTimeout(timeout));
750
872
  }
751
873
  // Start version check asynchronously
752
- checkForNewPiVersion(this.version).then((newRelease) => {
874
+ checkForNewApexCodeVersion(this.version).then((newRelease) => {
753
875
  if (newRelease) {
754
876
  this.showNewVersionNotification(newRelease);
755
877
  }
@@ -775,7 +897,18 @@ export class InteractiveMode {
775
897
  }
776
898
  });
777
899
  // Show startup warnings
778
- const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
900
+ const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
901
+ for (const diagnostic of startupDiagnostics ?? []) {
902
+ if (diagnostic.type === "error") {
903
+ this.showError(diagnostic.message);
904
+ }
905
+ else if (diagnostic.type === "warning") {
906
+ this.showWarning(diagnostic.message);
907
+ }
908
+ else {
909
+ this.showStatus(diagnostic.message);
910
+ }
911
+ }
779
912
  if (migratedProviders && migratedProviders.length > 0) {
780
913
  this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
781
914
  }
@@ -821,7 +954,7 @@ export class InteractiveMode {
821
954
  }
822
955
  }
823
956
  async checkForPackageUpdates() {
824
- if (process.env.PI_OFFLINE) {
957
+ if (getApexEnvironment("APEX_CODE_OFFLINE")) {
825
958
  return [];
826
959
  }
827
960
  try {
@@ -874,7 +1007,7 @@ export class InteractiveMode {
874
1007
  return "tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.";
875
1008
  }
876
1009
  if (extendedKeysFormat === "xterm") {
877
- return "tmux extended-keys-format is xterm. Pi works best with csi-u. Add `set -g extended-keys-format csi-u` to ~/.tmux.conf and restart tmux.";
1010
+ return "tmux extended-keys-format is xterm. Apex Code works best with csi-u. Add `set -g extended-keys-format csi-u` to ~/.tmux.conf and restart tmux.";
878
1011
  }
879
1012
  return undefined;
880
1013
  }
@@ -891,35 +1024,17 @@ export class InteractiveMode {
891
1024
  const changelogPath = getChangelogPath();
892
1025
  const entries = parseChangelog(changelogPath);
893
1026
  if (!lastVersion) {
894
- // Fresh install - record the version, send telemetry, don't show changelog
1027
+ // Fresh install - record the version, don't show changelog
895
1028
  this.settingsManager.setLastChangelogVersion(VERSION);
896
- this.reportInstallTelemetry(VERSION);
897
1029
  return undefined;
898
1030
  }
899
1031
  const newEntries = getNewEntries(entries, lastVersion);
900
1032
  if (newEntries.length > 0) {
901
1033
  this.settingsManager.setLastChangelogVersion(VERSION);
902
- this.reportInstallTelemetry(VERSION);
903
1034
  return newEntries.map((e) => normalizeChangelogLinks(e.content, e)).join("\n\n");
904
1035
  }
905
1036
  return undefined;
906
1037
  }
907
- reportInstallTelemetry(version) {
908
- if (process.env.PI_OFFLINE) {
909
- return;
910
- }
911
- if (!isInstallTelemetryEnabled(this.settingsManager)) {
912
- return;
913
- }
914
- void fetch(`https://pi.dev/api/report-install?version=${encodeURIComponent(version)}`, {
915
- headers: {
916
- "User-Agent": getPiUserAgent(version),
917
- },
918
- signal: AbortSignal.timeout(5000),
919
- })
920
- .then(() => undefined)
921
- .catch(() => undefined);
922
- }
923
1038
  getMarkdownThemeWithSettings() {
924
1039
  return {
925
1040
  ...getMarkdownTheme(),
@@ -1216,7 +1331,7 @@ export class InteractiveMode {
1216
1331
  showLoadedResources(options) {
1217
1332
  // Resource rendering is idempotent; chat clears no longer clear this separate container.
1218
1333
  this.loadedResourcesContainer.clear();
1219
- const showListing = options?.force || this.options.verbose || !this.settingsManager.getQuietStartup();
1334
+ const showListing = options?.force || this.options.verbose;
1220
1335
  const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
1221
1336
  if (!showListing && !showDiagnostics) {
1222
1337
  return;
@@ -1333,43 +1448,105 @@ export class InteractiveMode {
1333
1448
  addLoadedSection("Themes", themeCompactList, themeList);
1334
1449
  }
1335
1450
  }
1451
+ // Warnings collapse into the counted line when that line is on screen; the
1452
+ // full trace lives behind /resources. Errors always surface inline,
1453
+ // because an error means a resource did not load at all.
1336
1454
  if (showDiagnostics) {
1337
- const skillDiagnostics = skillsResult.diagnostics;
1338
- if (skillDiagnostics.length > 0) {
1339
- const warningLines = this.formatDiagnostics(skillDiagnostics, sourceInfos);
1340
- this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Skill conflicts]")}\n${warningLines}`, 0, 0));
1341
- this.loadedResourcesContainer.addChild(new Spacer(1));
1342
- }
1343
- const promptDiagnostics = promptsResult.diagnostics;
1344
- if (promptDiagnostics.length > 0) {
1345
- const warningLines = this.formatDiagnostics(promptDiagnostics, sourceInfos);
1346
- this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Prompt conflicts]")}\n${warningLines}`, 0, 0));
1347
- this.loadedResourcesContainer.addChild(new Spacer(1));
1348
- }
1349
- const extensionDiagnostics = [];
1350
- const extensionErrors = this.session.resourceLoader.getExtensions().errors;
1351
- if (extensionErrors.length > 0) {
1352
- for (const error of extensionErrors) {
1353
- extensionDiagnostics.push({ type: "error", message: error.error, path: error.path });
1354
- }
1355
- }
1356
- const commandDiagnostics = this.session.extensionRunner.getCommandDiagnostics();
1357
- extensionDiagnostics.push(...commandDiagnostics);
1358
- extensionDiagnostics.push(...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner));
1359
- const shortcutDiagnostics = this.session.extensionRunner.getShortcutDiagnostics();
1360
- extensionDiagnostics.push(...shortcutDiagnostics);
1361
- if (extensionDiagnostics.length > 0) {
1362
- const warningLines = this.formatDiagnostics(extensionDiagnostics, sourceInfos);
1363
- this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Extension issues]")}\n${warningLines}`, 0, 0));
1364
- this.loadedResourcesContainer.addChild(new Spacer(1));
1365
- }
1366
- const themeDiagnostics = themesResult.diagnostics;
1367
- if (themeDiagnostics.length > 0) {
1368
- const warningLines = this.formatDiagnostics(themeDiagnostics, sourceInfos);
1369
- this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Theme conflicts]")}\n${warningLines}`, 0, 0));
1455
+ const collapseWarnings = !showListing && this.startupInventoryVisible;
1456
+ for (const group of this.collectDiagnosticGroups()) {
1457
+ const visible = collapseWarnings
1458
+ ? group.diagnostics.filter((diagnostic) => diagnostic.type === "error")
1459
+ : group.diagnostics;
1460
+ if (visible.length === 0)
1461
+ continue;
1462
+ const severity = visible.some((diagnostic) => diagnostic.type === "error") ? "error" : "warning";
1463
+ const body = this.formatDiagnostics(visible, sourceInfos);
1464
+ this.loadedResourcesContainer.addChild(new Text(`${theme.fg(severity, `[${group.label}]`)}\n${body}`, 0, 0));
1370
1465
  this.loadedResourcesContainer.addChild(new Spacer(1));
1371
1466
  }
1372
1467
  }
1468
+ // Counts on the startup screen are read at render time, so they fill in
1469
+ // once resources finish loading without an explicit refresh here.
1470
+ this.builtInHeader?.invalidate?.();
1471
+ }
1472
+ /**
1473
+ * Every startup diagnostic, grouped by the resource kind that produced it.
1474
+ *
1475
+ * One list feeds both the counted line in the header and the detailed
1476
+ * listing, so the two can never disagree about how many issues exist.
1477
+ */
1478
+ collectDiagnosticGroups() {
1479
+ const extensionDiagnostics = [
1480
+ ...this.session.resourceLoader
1481
+ .getExtensions()
1482
+ .errors.map((error) => ({ type: "error", message: error.error, path: error.path })),
1483
+ ...this.session.extensionRunner.getCommandDiagnostics(),
1484
+ ...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner),
1485
+ ...this.session.extensionRunner.getShortcutDiagnostics(),
1486
+ ];
1487
+ return [
1488
+ { label: "Skill conflicts", diagnostics: this.session.resourceLoader.getSkills().diagnostics },
1489
+ { label: "Prompt conflicts", diagnostics: this.session.resourceLoader.getPrompts().diagnostics },
1490
+ { label: "Extension issues", diagnostics: extensionDiagnostics },
1491
+ { label: "Theme conflicts", diagnostics: this.session.resourceLoader.getThemes().diagnostics },
1492
+ ].filter((group) => group.diagnostics.length > 0);
1493
+ }
1494
+ /**
1495
+ * The counted line in the ruled band under the mark.
1496
+ *
1497
+ * The startup screen used to print all 152 skill names across fifteen rows.
1498
+ * A count answers the only question that screen can usefully answer, which
1499
+ * is what got loaded; the names live behind /resources now.
1500
+ */
1501
+ /**
1502
+ * The sigil row under the hint: how to reach commands, bash, and files.
1503
+ *
1504
+ * These are the only three characters that change what the composer does, so
1505
+ * they take the accent and everything around them stays quiet.
1506
+ */
1507
+ /** Live keybindings under the autocomplete dropdown. */
1508
+ buildAutocompleteFooter() {
1509
+ const separator = theme.fg("borderMuted", this.settingsManager.getSymbolPreset() === "ascii" ? " - " : " · ");
1510
+ return [
1511
+ rawKeyHint(`${keyText("tui.select.up")}/${keyText("tui.select.down")}`, "move"),
1512
+ keyHint("tui.select.confirm", "select"),
1513
+ keyHint("tui.input.tab", "complete"),
1514
+ keyHint("tui.select.cancel", "dismiss"),
1515
+ ].join(separator);
1516
+ }
1517
+ buildStartupShortcuts() {
1518
+ const entry = (sigil, label) => `${theme.fg("accent", sigil)} ${theme.fg("muted", label)}`;
1519
+ return [
1520
+ entry("/", "commands"),
1521
+ entry("!", "bash"),
1522
+ entry("@", "files"),
1523
+ `${theme.fg("dim", keyText("app.interrupt"))} ${theme.fg("muted", "interrupt")}`,
1524
+ ].join(" ");
1525
+ }
1526
+ buildStartupInventory() {
1527
+ const counted = (total, singular) => `${total} ${singular}${total === 1 ? "" : "s"}`;
1528
+ const parts = [];
1529
+ const skills = this.session.resourceLoader.getSkills().skills.length;
1530
+ const extensions = this.session.resourceLoader.getExtensions().extensions.filter((e) => !e.hidden).length;
1531
+ const prompts = this.session.resourceLoader.getPrompts().prompts.length;
1532
+ if (skills > 0)
1533
+ parts.push(theme.fg("muted", counted(skills, "skill")));
1534
+ if (extensions > 0)
1535
+ parts.push(theme.fg("muted", counted(extensions, "extension")));
1536
+ if (prompts > 0)
1537
+ parts.push(theme.fg("muted", counted(prompts, "prompt")));
1538
+ const diagnostics = this.collectDiagnosticGroups().flatMap((group) => group.diagnostics);
1539
+ const errors = diagnostics.filter((diagnostic) => diagnostic.type === "error").length;
1540
+ const conflicts = diagnostics.length - errors;
1541
+ if (errors > 0)
1542
+ parts.push(theme.fg("error", counted(errors, "error")));
1543
+ if (conflicts > 0)
1544
+ parts.push(theme.fg("warning", counted(conflicts, "conflict")));
1545
+ if (parts.length === 0)
1546
+ return undefined;
1547
+ const ascii = this.settingsManager.getSymbolPreset() === "ascii";
1548
+ const separator = theme.fg("borderMuted", ascii ? " - " : " · ");
1549
+ return parts.join(separator);
1373
1550
  }
1374
1551
  /**
1375
1552
  * Initialize the extension system with TUI-based UI context.
@@ -1457,6 +1634,7 @@ export class InteractiveMode {
1457
1634
  this.applyFullscreenScrollbarSetting();
1458
1635
  this.footer.setSession(this.session);
1459
1636
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
1637
+ void this.refreshFooterPermissionMode();
1460
1638
  this.footerDataProvider.setCwd(this.sessionManager.getCwd());
1461
1639
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
1462
1640
  this.outputPad = this.settingsManager.getOutputPad();
@@ -1499,7 +1677,7 @@ export class InteractiveMode {
1499
1677
  const message = error instanceof Error ? error.message : String(error);
1500
1678
  this.showError(`${prefix}: ${message}`);
1501
1679
  stopThemeWatcher();
1502
- this.stop();
1680
+ this.stop("transcript");
1503
1681
  process.exit(1);
1504
1682
  }
1505
1683
  renderCurrentSessionState() {
@@ -1509,9 +1687,23 @@ export class InteractiveMode {
1509
1687
  this.compactionQueuedMessages = [];
1510
1688
  this.streamingComponent = undefined;
1511
1689
  this.streamingMessage = undefined;
1512
- this.pendingTools.clear();
1690
+ // The clears above detach children without stopping their animation
1691
+ // timers. Bash components embed an animated loader; dispose them
1692
+ // explicitly so an in-flight `!cmd` cannot keep rendering (or get
1693
+ // flushed back into a fresh session by flushPendingBashComponents).
1694
+ for (const component of this.pendingBashComponents)
1695
+ component.dispose();
1696
+ this.pendingBashComponents = [];
1697
+ this.bashComponent?.dispose();
1698
+ this.bashComponent = undefined;
1699
+ this.clearPendingTools();
1513
1700
  this.renderInitialMessages();
1514
1701
  }
1702
+ clearPendingTools() {
1703
+ for (const component of this.pendingTools.values())
1704
+ component.dispose();
1705
+ this.pendingTools.clear();
1706
+ }
1515
1707
  /**
1516
1708
  * Get a registered tool definition by name (for custom rendering).
1517
1709
  */
@@ -1894,7 +2086,7 @@ export class InteractiveMode {
1894
2086
  /**
1895
2087
  * Show a selector for extensions.
1896
2088
  */
1897
- showExtensionSelector(title, options, opts) {
2089
+ showExtensionSelector(title, options, opts, enableSearch = false) {
1898
2090
  return new Promise((resolve) => {
1899
2091
  if (opts?.signal?.aborted) {
1900
2092
  resolve(undefined);
@@ -1913,7 +2105,12 @@ export class InteractiveMode {
1913
2105
  opts?.signal?.removeEventListener("abort", onAbort);
1914
2106
  this.hideExtensionSelector();
1915
2107
  resolve(undefined);
1916
- }, { tui: this.ui, timeout: opts?.timeout, onToggleToolsExpanded: () => this.toggleToolOutputExpansion() });
2108
+ }, {
2109
+ tui: this.ui,
2110
+ timeout: opts?.timeout,
2111
+ onToggleToolsExpanded: () => this.toggleToolOutputExpansion(),
2112
+ enableSearch,
2113
+ });
1917
2114
  this.disposeActiveSelector();
1918
2115
  this.editorContainer.clear();
1919
2116
  this.editorContainer.addChild(this.extensionSelector);
@@ -1932,6 +2129,54 @@ export class InteractiveMode {
1932
2129
  this.ui.setFocus(this.editor);
1933
2130
  this.ui.requestRender();
1934
2131
  }
2132
+ async showConfigurationIndex() {
2133
+ const choice = await this.showExtensionSelector("Configuration", [
2134
+ "Settings",
2135
+ "Model",
2136
+ "Model cycling",
2137
+ "Provider login",
2138
+ "Provider logout",
2139
+ "Project trust",
2140
+ "Resources, extensions, and MCP adapters",
2141
+ ], undefined, true);
2142
+ switch (choice) {
2143
+ case "Settings":
2144
+ await this.showSettingsSelector();
2145
+ break;
2146
+ case "Model":
2147
+ this.showModelSelector();
2148
+ break;
2149
+ case "Model cycling":
2150
+ await this.showModelsSelector();
2151
+ break;
2152
+ case "Provider login":
2153
+ await this.handleLoginCommand();
2154
+ break;
2155
+ case "Provider logout":
2156
+ this.showOAuthSelector("logout");
2157
+ break;
2158
+ case "Project trust":
2159
+ this.showTrustSelector();
2160
+ break;
2161
+ case "Resources, extensions, and MCP adapters":
2162
+ this.showStatus(this.describeMcpServers());
2163
+ break;
2164
+ }
2165
+ }
2166
+ /**
2167
+ * Report configured MCP servers and their live connection state. Replaces a string
2168
+ * that offered MCP management from `apex-code config`, which manages package
2169
+ * resources and has never been able to configure a server.
2170
+ */
2171
+ describeMcpServers() {
2172
+ const runtime = this.session.mcpRuntime;
2173
+ const servers = runtime ? [...runtime.servers.keys()] : [];
2174
+ if (servers.length === 0) {
2175
+ return "Run apex-code config to manage resources and extensions. No MCP server is configured; add one to .mcp.json.";
2176
+ }
2177
+ const states = servers.map((name) => `${name} (${runtime?.manager.state(name).kind ?? "disconnected"})`);
2178
+ return `Run apex-code config to manage resources and extensions. MCP servers: ${states.join(", ")}.`;
2179
+ }
1935
2180
  /**
1936
2181
  * Show a confirmation dialog for extensions.
1937
2182
  */
@@ -2243,6 +2488,8 @@ export class InteractiveMode {
2243
2488
  const wasBashMode = this.isBashMode;
2244
2489
  this.isBashMode = text.trimStart().startsWith("!");
2245
2490
  if (wasBashMode !== this.isBashMode) {
2491
+ if (this.isBashMode)
2492
+ this.offerFirstUseHint("bash");
2246
2493
  this.updateEditorBorderColor();
2247
2494
  }
2248
2495
  };
@@ -2274,7 +2521,7 @@ export class InteractiveMode {
2274
2521
  if (image) {
2275
2522
  const tmpDir = os.tmpdir();
2276
2523
  const ext = extensionForImageMimeType(image.mimeType) ?? "png";
2277
- const fileName = `pi-clipboard-${crypto.randomUUID()}.${ext}`;
2524
+ const fileName = `apex-code-clipboard-${crypto.randomUUID()}.${ext}`;
2278
2525
  const filePath = path.join(tmpDir, fileName);
2279
2526
  fs.writeFileSync(filePath, Buffer.from(image.bytes));
2280
2527
  this.editor.insertTextAtCursor?.(filePath);
@@ -2291,15 +2538,31 @@ export class InteractiveMode {
2291
2538
  // Silently ignore clipboard errors (may not have permission, etc.)
2292
2539
  }
2293
2540
  }
2541
+ handleStartupSubmit(text) {
2542
+ this.editor.setText(text);
2543
+ this.showStatus("Startup is still in progress");
2544
+ }
2294
2545
  setupEditorSubmitHandler() {
2295
2546
  this.defaultEditor.onSubmit = async (text) => {
2296
2547
  text = text.trim();
2297
2548
  if (!text)
2298
2549
  return;
2550
+ // A mode chosen in /settings is written asynchronously. Awaiting it here
2551
+ // keeps the store authoritative before any tool call can read it, so a
2552
+ // switch *out* of bypassPermissions can never be raced by the next turn.
2553
+ if (this.pendingPermissionModeWrite) {
2554
+ await this.pendingPermissionModeWrite;
2555
+ this.pendingPermissionModeWrite = undefined;
2556
+ }
2299
2557
  // Handle commands
2558
+ if (text === "/config") {
2559
+ this.editor.setText("");
2560
+ await this.showConfigurationIndex();
2561
+ return;
2562
+ }
2300
2563
  if (text === "/settings") {
2301
- this.showSettingsSelector();
2302
2564
  this.editor.setText("");
2565
+ await this.showSettingsSelector();
2303
2566
  return;
2304
2567
  }
2305
2568
  if (text === "/scoped-models") {
@@ -2313,6 +2576,12 @@ export class InteractiveMode {
2313
2576
  await this.handleModelCommand(searchTerm);
2314
2577
  return;
2315
2578
  }
2579
+ if (text === "/thinking" || text.startsWith("/thinking ")) {
2580
+ const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
2581
+ this.editor.setText("");
2582
+ this.handleThinkingCommand(searchTerm);
2583
+ return;
2584
+ }
2316
2585
  if (text === "/export" || text.startsWith("/export ")) {
2317
2586
  await this.handleExportCommand(text);
2318
2587
  this.editor.setText("");
@@ -2339,7 +2608,7 @@ export class InteractiveMode {
2339
2608
  return;
2340
2609
  }
2341
2610
  if (text === "/session") {
2342
- this.handleSessionCommand();
2611
+ await this.handleSessionCommand();
2343
2612
  this.editor.setText("");
2344
2613
  return;
2345
2614
  }
@@ -2348,11 +2617,21 @@ export class InteractiveMode {
2348
2617
  this.editor.setText("");
2349
2618
  return;
2350
2619
  }
2620
+ if (text === "/resources") {
2621
+ this.showLoadedResources({ force: true });
2622
+ this.editor.setText("");
2623
+ return;
2624
+ }
2351
2625
  if (text === "/hotkeys") {
2352
2626
  this.handleHotkeysCommand();
2353
2627
  this.editor.setText("");
2354
2628
  return;
2355
2629
  }
2630
+ if (text === "/help") {
2631
+ this.handleHelpCommand();
2632
+ this.editor.setText("");
2633
+ return;
2634
+ }
2356
2635
  if (text === "/fork") {
2357
2636
  this.showUserMessageSelector();
2358
2637
  this.editor.setText("");
@@ -2488,7 +2767,7 @@ export class InteractiveMode {
2488
2767
  this.footer.invalidate();
2489
2768
  switch (event.type) {
2490
2769
  case "agent_start":
2491
- this.pendingTools.clear();
2770
+ this.clearPendingTools();
2492
2771
  if (this.settingsManager.getShowTerminalProgress()) {
2493
2772
  this.ui.terminal.setProgress(true);
2494
2773
  }
@@ -2540,6 +2819,7 @@ export class InteractiveMode {
2540
2819
  this.streamingMessage = event.message;
2541
2820
  this.chatContainer.addChild(this.streamingComponent);
2542
2821
  this.streamingComponent.updateContent(this.streamingMessage, true);
2822
+ this.updateEditorBorderColor();
2543
2823
  this.ui.requestRender();
2544
2824
  }
2545
2825
  break;
@@ -2557,6 +2837,7 @@ export class InteractiveMode {
2557
2837
  component.setExpanded(this.toolOutputExpanded);
2558
2838
  this.chatContainer.addChild(component);
2559
2839
  this.pendingTools.set(content.id, component);
2840
+ this.offerFirstUseHint("tool-expand");
2560
2841
  }
2561
2842
  else {
2562
2843
  const component = this.pendingTools.get(content.id);
@@ -2594,7 +2875,7 @@ export class InteractiveMode {
2594
2875
  isError: true,
2595
2876
  });
2596
2877
  }
2597
- this.pendingTools.clear();
2878
+ this.clearPendingTools();
2598
2879
  }
2599
2880
  else {
2600
2881
  // Args are now complete - trigger diff computation for edit tools
@@ -2606,6 +2887,7 @@ export class InteractiveMode {
2606
2887
  this.streamingComponent = undefined;
2607
2888
  this.streamingMessage = undefined;
2608
2889
  this.footer.invalidate();
2890
+ this.updateEditorBorderColor();
2609
2891
  }
2610
2892
  this.ui.requestRender();
2611
2893
  break;
@@ -2654,7 +2936,7 @@ export class InteractiveMode {
2654
2936
  this.streamingComponent = undefined;
2655
2937
  this.streamingMessage = undefined;
2656
2938
  }
2657
- this.pendingTools.clear();
2939
+ this.clearPendingTools();
2658
2940
  this.ui.requestRender();
2659
2941
  break;
2660
2942
  case "agent_settled":
@@ -2691,9 +2973,21 @@ export class InteractiveMode {
2691
2973
  }
2692
2974
  }
2693
2975
  else if (event.result) {
2976
+ const entries = this.sessionManager.buildContextEntries();
2977
+ if (entries[0]?.type !== "compaction") {
2978
+ throw new Error("Completed compaction is missing from the session context");
2979
+ }
2694
2980
  this.chatContainer.clear();
2695
- this.rebuildChatFromMessages();
2981
+ // The latest compaction is prepended for model context; append it below at its chronological position.
2982
+ this.renderSessionEntries(entries.slice(1));
2696
2983
  this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
2984
+ if (event.result.usage) {
2985
+ this.addCompactionCostNotice({
2986
+ type: "compaction_cost",
2987
+ kind: "compaction",
2988
+ usage: event.result.usage,
2989
+ });
2990
+ }
2697
2991
  this.footer.invalidate();
2698
2992
  }
2699
2993
  else if (event.errorMessage) {
@@ -2766,6 +3060,19 @@ export class InteractiveMode {
2766
3060
  : message.content.filter((c) => c.type === "text");
2767
3061
  return textBlocks.map((c) => c.text).join("");
2768
3062
  }
3063
+ /** Show a managed-tool status update in the chat. */
3064
+ showManagedToolStatus(status) {
3065
+ if (!this.managedToolStatusStarted) {
3066
+ this.chatContainer.addChild(new Spacer(1));
3067
+ this.managedToolStatusStarted = true;
3068
+ }
3069
+ const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
3070
+ const color = status.type === "warning" ? "warning" : "dim";
3071
+ this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
3072
+ this.lastStatusSpacer = undefined;
3073
+ this.lastStatusText = undefined;
3074
+ this.ui.requestRender();
3075
+ }
2769
3076
  /**
2770
3077
  * Show a status message in the chat.
2771
3078
  *
@@ -2886,7 +3193,7 @@ export class InteractiveMode {
2886
3193
  }
2887
3194
  }
2888
3195
  renderSessionItems(items, options = {}) {
2889
- this.pendingTools.clear();
3196
+ this.clearPendingTools();
2890
3197
  const renderedPendingTools = new Map();
2891
3198
  // Cache-miss notices are not persisted; re-derive them from the full entry
2892
3199
  // list and re-inject them after the assistant messages that paid for them.
@@ -2902,6 +3209,10 @@ export class InteractiveMode {
2902
3209
  this.addCustomEntryToChat(item);
2903
3210
  continue;
2904
3211
  }
3212
+ if (isCompactionCostNotice(item)) {
3213
+ this.addCompactionCostNotice(item);
3214
+ continue;
3215
+ }
2905
3216
  const message = item;
2906
3217
  // Assistant messages need special handling for tool calls
2907
3218
  if (message.role === "assistant") {
@@ -2969,10 +3280,28 @@ export class InteractiveMode {
2969
3280
  if (entry.type === "custom") {
2970
3281
  return [entry];
2971
3282
  }
2972
- return sessionEntryToContextMessages(entry);
3283
+ const messages = sessionEntryToContextMessages(entry);
3284
+ if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
3285
+ return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
3286
+ }
3287
+ return messages;
2973
3288
  });
2974
3289
  this.renderSessionItems(items, options);
2975
3290
  }
3291
+ /**
3292
+ * Render billing usage for a compaction or branch summary. The notice is derived
3293
+ * from persisted summary usage and is not stored as a separate session entry.
3294
+ */
3295
+ addCompactionCostNotice(notice) {
3296
+ if (!this.settingsManager.getShowCacheMissNotices())
3297
+ return;
3298
+ const { usage } = notice;
3299
+ const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
3300
+ const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
3301
+ const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
3302
+ this.chatContainer.addChild(new Spacer(1));
3303
+ this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
3304
+ }
2976
3305
  /**
2977
3306
  * Show a transcript notice when a completed assistant message paid for a
2978
3307
  * significant cache miss. Only states observable facts: the miss itself,
@@ -3024,7 +3353,7 @@ export class InteractiveMode {
3024
3353
  if (this.chatContainer.children.length > 0) {
3025
3354
  this.chatContainer.addChild(new Spacer(1));
3026
3355
  }
3027
- this.chatContainer.addChild(new Text(theme.fg("warning", `This project is not trusted. Project ${CONFIG_DIR_NAME} resources and packages are ignored. Use /trust to save a trust decision, then restart pi.`), 1, 0));
3356
+ this.chatContainer.addChild(new Text(theme.fg("warning", `This project is not trusted. Project ${CONFIG_DIR_NAME} resources and packages are ignored. Use /trust to save a trust decision, then restart apex-code.`), 1, 0));
3028
3357
  }
3029
3358
  async getUserInput() {
3030
3359
  const queuedInput = this.pendingUserInputs.shift();
@@ -3137,7 +3466,7 @@ export class InteractiveMode {
3137
3466
  this.ui.stop();
3138
3467
  }
3139
3468
  catch { }
3140
- console.error("pi exiting due to uncaughtException:");
3469
+ console.error("apex-code exiting due to uncaughtException:");
3141
3470
  console.error(error);
3142
3471
  process.exit(1);
3143
3472
  }
@@ -3183,6 +3512,34 @@ export class InteractiveMode {
3183
3512
  const uncaughtExceptionHandler = (error) => this.uncaughtCrash(error);
3184
3513
  process.prependListener("uncaughtException", uncaughtExceptionHandler);
3185
3514
  this.signalCleanupHandlers.push(() => process.off("uncaughtException", uncaughtExceptionHandler));
3515
+ // Lend the terminal back to the supervisor when it needs to draw a sandbox
3516
+ // escalation prompt. The supervisor owns that decision (ADR 0023) and this
3517
+ // process cannot read the answer anyway -- bwrap --new-session leaves it with no
3518
+ // controlling terminal. `ui.stop()` is the same restore Ctrl+Z already performs,
3519
+ // so the terminal is in cooked mode with a visible cursor while the human answers.
3520
+ const handoffDirectory = process.env[TERMINAL_HANDOFF_PATH_VARIABLE];
3521
+ if (handoffDirectory) {
3522
+ const observer = observeTerminalHandoff(handoffDirectory, {
3523
+ suspend: () => {
3524
+ try {
3525
+ this.ui.stop();
3526
+ }
3527
+ catch {
3528
+ // A terminal we cannot restore still leaves the prompt readable.
3529
+ }
3530
+ },
3531
+ resume: () => {
3532
+ try {
3533
+ this.ui.start();
3534
+ this.ui.requestRender(true);
3535
+ }
3536
+ catch {
3537
+ // Nothing further to try; the next render request recovers.
3538
+ }
3539
+ },
3540
+ });
3541
+ this.signalCleanupHandlers.push(() => observer.stop());
3542
+ }
3186
3543
  }
3187
3544
  unregisterSignalHandlers() {
3188
3545
  for (const cleanup of this.signalCleanupHandlers) {
@@ -3244,6 +3601,7 @@ export class InteractiveMode {
3244
3601
  this.editor.addToHistory?.(text);
3245
3602
  this.editor.setText("");
3246
3603
  await this.session.prompt(text, { streamingBehavior: "followUp" });
3604
+ this.offerFirstUseHint("queue");
3247
3605
  this.updatePendingMessagesDisplay();
3248
3606
  this.ui.requestRender();
3249
3607
  }
@@ -3263,6 +3621,7 @@ export class InteractiveMode {
3263
3621
  }
3264
3622
  }
3265
3623
  updateEditorBorderColor() {
3624
+ this.defaultEditor.setModeLabel(this.isBashMode ? "bash" : this.session.isStreaming ? "busy" : undefined);
3266
3625
  if (this.isBashMode) {
3267
3626
  this.editor.borderColor = theme.getBashModeBorderColor();
3268
3627
  }
@@ -3323,6 +3682,7 @@ export class InteractiveMode {
3323
3682
  this.showStatus(`Tool output: ${expanded ? "expanded" : "collapsed"}`);
3324
3683
  }
3325
3684
  toggleThinkingBlockVisibility() {
3685
+ this.offerFirstUseHint("thinking");
3326
3686
  this.hideThinkingBlock = !this.hideThinkingBlock;
3327
3687
  this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
3328
3688
  // Rebuild chat from session messages
@@ -3374,7 +3734,7 @@ export class InteractiveMode {
3374
3734
  showNewVersionNotification(release) {
3375
3735
  const action = theme.fg("accent", `${APP_NAME} update`);
3376
3736
  const updateInstruction = theme.fg("muted", `New version ${release.version} is available. Run `) + action;
3377
- const changelogUrl = "https://pi.dev/changelog";
3737
+ const changelogUrl = "https://github.com/Fchery87/apex-code/blob/main/packages/coding-agent/CHANGELOG.md";
3378
3738
  const changelogLink = getCapabilities().hyperlinks
3379
3739
  ? hyperlink(theme.fg("accent", changelogUrl), changelogUrl)
3380
3740
  : theme.fg("accent", changelogUrl);
@@ -3606,11 +3966,63 @@ export class InteractiveMode {
3606
3966
  this.ui.setFocus(created.focus);
3607
3967
  this.ui.requestRender();
3608
3968
  }
3609
- showSettingsSelector() {
3969
+ /**
3970
+ * Resyncs the footer from the store. Called on every session swap, not just at
3971
+ * startup: `applyRuntimeSettings` also repoints the cwd, and cwd selects which
3972
+ * project-scope permission file applies, so the mode genuinely can differ
3973
+ * between two sessions in one process. A footer left showing the old value
3974
+ * would under-report bypassPermissions, which is the one direction that matters.
3975
+ */
3976
+ async refreshFooterPermissionMode() {
3977
+ const resolution = await this.session.getPermissionMode();
3978
+ this.footer.setPermissionMode(resolution?.mode ?? "default");
3979
+ if (this.isInitialized) {
3980
+ this.ui.requestRender();
3981
+ }
3982
+ }
3983
+ /**
3984
+ * Writes the mode to user scope, then reports what is actually in force. The
3985
+ * write can be outranked by `--permission-mode` or a project file, and a
3986
+ * settings row that silently does nothing is worse than no row at all.
3987
+ */
3988
+ async applyPermissionMode(mode) {
3989
+ let resolution;
3990
+ try {
3991
+ const write = this.session.setPermissionMode(mode);
3992
+ this.pendingPermissionModeWrite = write.then(() => { }, () => { });
3993
+ resolution = await write;
3994
+ }
3995
+ catch (error) {
3996
+ // Never swallow this. A permission mode the user believes they set, and
3997
+ // did not, is the one outcome worse than the prompt they were escaping.
3998
+ const detail = error instanceof Error ? error.message : String(error);
3999
+ this.chatContainer.addChild(new Text(theme.fg("error", `Permission mode could not be saved: ${detail}`), 1, 0));
4000
+ this.ui.requestRender();
4001
+ return;
4002
+ }
4003
+ if (!resolution)
4004
+ return;
4005
+ const outranked = resolution.mode !== mode;
4006
+ const source = PERMISSION_MODE_OVERRIDE_HINTS[resolution.origin] ?? resolution.origin;
4007
+ const line = outranked
4008
+ ? `Permission mode saved as ${mode}, but ${source} sets ${resolution.mode} for this session.`
4009
+ : `Permission mode set to ${mode}.`;
4010
+ this.chatContainer.addChild(new Text(theme.fg(outranked ? "warning" : "muted", line), 1, 0));
4011
+ this.footer.setPermissionMode(resolution.mode);
4012
+ this.ui.requestRender();
4013
+ }
4014
+ async showSettingsSelector() {
4015
+ const permissionMode = await this.session.getPermissionMode();
3610
4016
  this.showSelector((done) => {
3611
4017
  let selector;
4018
+ const defaultProvider = this.settingsManager.getDefaultProvider();
4019
+ const defaultModelId = this.settingsManager.getDefaultModel();
4020
+ const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
3612
4021
  selector = new SettingsSelectorComponent({
3613
4022
  autoCompact: this.session.autoCompactionEnabled,
4023
+ defaultModel,
4024
+ currentModel: this.session.model,
4025
+ availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
3614
4026
  showImages: this.settingsManager.getShowImages(),
3615
4027
  imageWidthCells: this.settingsManager.getImageWidthCells(),
3616
4028
  autoResizeImages: this.settingsManager.getImageAutoResize(),
@@ -3620,20 +4032,22 @@ export class InteractiveMode {
3620
4032
  followUpMode: this.session.followUpMode,
3621
4033
  transport: this.settingsManager.getTransport(),
3622
4034
  httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
3623
- thinkingLevel: this.session.thinkingLevel,
3624
- availableThinkingLevels: this.session.getAvailableThinkingLevels(),
3625
- currentTheme: this.settingsManager.getThemeSetting() || "dark",
4035
+ thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
4036
+ availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
4037
+ modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
4038
+ currentTheme: this.themeController.getThemeSelection() || "dark",
3626
4039
  terminalTheme: this.themeController.getTerminalTheme(),
3627
4040
  availableThemes: getAvailableThemes(),
3628
4041
  hideThinkingBlock: this.hideThinkingBlock,
3629
4042
  mermaidRenderingMode: this.settingsManager.getMermaidRenderingMode(),
3630
4043
  collapseChangelog: this.settingsManager.getCollapseChangelog(),
3631
- enableInstallTelemetry: this.settingsManager.getEnableInstallTelemetry(),
4044
+ sendProviderAttribution: this.settingsManager.getSendProviderAttribution(),
3632
4045
  doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
3633
4046
  treeFilterMode: this.settingsManager.getTreeFilterMode(),
3634
4047
  showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
3635
4048
  showCacheMissNotices: this.settingsManager.getShowCacheMissNotices(),
3636
4049
  defaultProjectTrust: this.settingsManager.getDefaultProjectTrust(),
4050
+ permissionMode,
3637
4051
  editorPaddingX: this.settingsManager.getEditorPaddingX(),
3638
4052
  outputPad: this.settingsManager.getOutputPad(),
3639
4053
  autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
@@ -3641,6 +4055,7 @@ export class InteractiveMode {
3641
4055
  clearOnShrink: this.settingsManager.getClearOnShrink(),
3642
4056
  showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
3643
4057
  tuiMode: this.ui.mode,
4058
+ fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
3644
4059
  fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
3645
4060
  warnings: this.settingsManager.getWarnings(),
3646
4061
  }, {
@@ -3689,14 +4104,30 @@ export class InteractiveMode {
3689
4104
  configureHttpDispatcher(timeoutMs);
3690
4105
  this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
3691
4106
  },
3692
- onThinkingLevelChange: (level) => {
3693
- this.session.setThinkingLevel(level);
3694
- this.footer.invalidate();
3695
- this.updateEditorBorderColor();
4107
+ onModelThinkingLevelChange: (provider, modelId, level) => {
4108
+ this.settingsManager.setModelThinkingLevel(provider, modelId, level);
4109
+ // If the override is for the current model, apply it to the session too
4110
+ const current = this.session.model;
4111
+ if (current && current.provider === provider && current.id === modelId) {
4112
+ this.session.setThinkingLevel(level);
4113
+ this.footer.invalidate();
4114
+ this.updateEditorBorderColor();
4115
+ }
4116
+ },
4117
+ onModelThinkingLevelRemove: (provider, modelId) => {
4118
+ this.settingsManager.removeModelThinkingLevel(provider, modelId);
4119
+ // If the override was for the current model, revert to global default
4120
+ const current = this.session.model;
4121
+ if (current && current.provider === provider && current.id === modelId) {
4122
+ const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
4123
+ this.session.setThinkingLevel(globalDefault);
4124
+ this.footer.invalidate();
4125
+ this.updateEditorBorderColor();
4126
+ }
3696
4127
  },
3697
4128
  onThemeChange: (themeSetting) => {
3698
4129
  this.settingsManager.setTheme(themeSetting);
3699
- void this.themeController.applyFromSettings();
4130
+ void this.themeController.setThemeSetting(themeSetting);
3700
4131
  },
3701
4132
  onThemePreview: (themeName) => this.themeController.preview(themeName),
3702
4133
  onHideThinkingBlockChange: (hidden) => {
@@ -3722,8 +4153,8 @@ export class InteractiveMode {
3722
4153
  onCollapseChangelogChange: (collapsed) => {
3723
4154
  this.settingsManager.setCollapseChangelog(collapsed);
3724
4155
  },
3725
- onEnableInstallTelemetryChange: (enabled) => {
3726
- this.settingsManager.setEnableInstallTelemetry(enabled);
4156
+ onSendProviderAttributionChange: (enabled) => {
4157
+ this.settingsManager.setSendProviderAttribution(enabled);
3727
4158
  },
3728
4159
  onQuietStartupChange: (enabled) => {
3729
4160
  this.settingsManager.setQuietStartup(enabled);
@@ -3731,6 +4162,9 @@ export class InteractiveMode {
3731
4162
  onDefaultProjectTrustChange: (defaultProjectTrust) => {
3732
4163
  this.settingsManager.setDefaultProjectTrust(defaultProjectTrust);
3733
4164
  },
4165
+ onPermissionModeChange: (mode) => {
4166
+ void this.applyPermissionMode(mode);
4167
+ },
3734
4168
  onDoubleEscapeActionChange: (action) => {
3735
4169
  this.settingsManager.setDoubleEscapeAction(action);
3736
4170
  },
@@ -3795,6 +4229,9 @@ export class InteractiveMode {
3795
4229
  this.statusContainer.clear();
3796
4230
  this.showStatus(`TUI mode: ${mode}`);
3797
4231
  },
4232
+ onFullscreenExitOutputChange: (output) => {
4233
+ this.settingsManager.setFullscreenExitOutput(output);
4234
+ },
3798
4235
  onFullscreenScrollbarChange: (mode) => {
3799
4236
  this.settingsManager.setFullscreenScrollbar(mode);
3800
4237
  this.applyFullscreenScrollbarSetting();
@@ -3810,6 +4247,44 @@ export class InteractiveMode {
3810
4247
  return { component: selector, focus: selector.getSettingsList() };
3811
4248
  });
3812
4249
  }
4250
+ handleThinkingCommand(searchTerm) {
4251
+ const availableLevels = this.session.getAvailableThinkingLevels();
4252
+ if (!searchTerm) {
4253
+ this.showThinkingSelector();
4254
+ return;
4255
+ }
4256
+ const normalized = searchTerm.trim().toLowerCase();
4257
+ const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
4258
+ if (!level) {
4259
+ this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
4260
+ return;
4261
+ }
4262
+ this.selectThinkingLevel(level, false);
4263
+ }
4264
+ selectThinkingLevel(level, persist) {
4265
+ try {
4266
+ this.session.setThinkingLevel(level, { persist });
4267
+ this.footer.invalidate();
4268
+ this.updateEditorBorderColor();
4269
+ this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
4270
+ }
4271
+ catch (error) {
4272
+ this.showError(error instanceof Error ? error.message : String(error));
4273
+ }
4274
+ }
4275
+ showThinkingSelector() {
4276
+ this.showSelector((done) => {
4277
+ const selectLevel = (level, persist) => {
4278
+ this.selectThinkingLevel(level, persist);
4279
+ done();
4280
+ };
4281
+ const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
4282
+ done();
4283
+ this.ui.requestRender();
4284
+ }, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
4285
+ return { component: selector, focus: selector };
4286
+ });
4287
+ }
3813
4288
  async handleModelCommand(searchTerm) {
3814
4289
  if (!searchTerm) {
3815
4290
  this.showModelSelector();
@@ -3818,7 +4293,7 @@ export class InteractiveMode {
3818
4293
  const model = await this.findExactModelMatch(searchTerm);
3819
4294
  if (model) {
3820
4295
  try {
3821
- await this.session.setModel(model);
4296
+ await this.session.setModel(model, { persist: false });
3822
4297
  this.footer.invalidate();
3823
4298
  this.updateEditorBorderColor();
3824
4299
  this.showStatus(`Model: ${model.id}`);
@@ -3847,7 +4322,7 @@ export class InteractiveMode {
3847
4322
  controller.abort();
3848
4323
  }, 15_000);
3849
4324
  try {
3850
- const result = await this.session.modelRuntime.refresh({ signal: controller.signal });
4325
+ const result = await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
3851
4326
  if (result.aborted && timedOut) {
3852
4327
  this.showWarning("Model refresh timed out; searching cached models.");
3853
4328
  }
@@ -3935,7 +4410,7 @@ export class InteractiveMode {
3935
4410
  onSelect: (selection) => {
3936
4411
  trustStore.setMany(selection.updates);
3937
4412
  done();
3938
- this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart pi for this to take effect.`);
4413
+ this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart ${APP_NAME} for this to take effect.`);
3939
4414
  },
3940
4415
  onCancel: () => {
3941
4416
  done();
@@ -3947,13 +4422,13 @@ export class InteractiveMode {
3947
4422
  }
3948
4423
  showModelSelector(initialSearchInput) {
3949
4424
  this.showSelector((done) => {
3950
- const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRuntime, this.session.scopedModels, async (model) => {
4425
+ const selectModel = async (model, persist) => {
3951
4426
  try {
3952
- await this.session.setModel(model);
4427
+ await this.session.setModel(model, { persist });
3953
4428
  this.footer.invalidate();
3954
4429
  this.updateEditorBorderColor();
3955
4430
  done();
3956
- this.showStatus(`Model: ${model.id}`);
4431
+ this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
3957
4432
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
3958
4433
  this.checkDaxnutsEasterEgg(model);
3959
4434
  }
@@ -3961,10 +4436,13 @@ export class InteractiveMode {
3961
4436
  done();
3962
4437
  this.showError(error instanceof Error ? error.message : String(error));
3963
4438
  }
3964
- }, () => {
4439
+ };
4440
+ const defaultProvider = this.settingsManager.getDefaultProvider();
4441
+ const defaultModel = this.settingsManager.getDefaultModel();
4442
+ const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
3965
4443
  done();
3966
4444
  this.ui.requestRender();
3967
- }, initialSearchInput);
4445
+ }, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
3968
4446
  return { component: selector, focus: selector, dispose: () => selector.dispose() };
3969
4447
  });
3970
4448
  }
@@ -4035,8 +4513,7 @@ export class InteractiveMode {
4035
4513
  this.ui.requestRender();
4036
4514
  },
4037
4515
  });
4038
- void this.session.modelRuntime
4039
- .refresh({ signal: controller.signal })
4516
+ void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
4040
4517
  .then((result) => {
4041
4518
  if (disposed)
4042
4519
  return;
@@ -4521,7 +4998,11 @@ export class InteractiveMode {
4521
4998
  if (isUnknownModel(previousModel)) {
4522
4999
  const availableModels = this.session.modelRuntime.getAvailableSnapshot();
4523
5000
  const providerModels = availableModels.filter((model) => model.provider === providerId);
4524
- if (!hasDefaultModelProvider(providerId)) {
5001
+ // Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
5002
+ if (providerId === "llama.cpp") {
5003
+ selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
5004
+ }
5005
+ else if (!hasDefaultModelProvider(providerId)) {
4525
5006
  selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
4526
5007
  }
4527
5008
  else if (providerModels.length === 0) {
@@ -4535,7 +5016,7 @@ export class InteractiveMode {
4535
5016
  }
4536
5017
  else {
4537
5018
  try {
4538
- await this.session.setModel(selectedModel);
5019
+ await this.session.setModel(selectedModel, { persist: true });
4539
5020
  }
4540
5021
  catch (error) {
4541
5022
  selectedModel = undefined;
@@ -4590,7 +5071,7 @@ export class InteractiveMode {
4590
5071
  this.ui.requestRender();
4591
5072
  };
4592
5073
  const dialog = new LoginDialogComponent(this.ui, providerOption.id, () => restoreEditor(), providerOption.name, `${providerOption.name} setup`);
4593
- dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside pi.`, [], true);
5074
+ dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside Apex Code.`, [], true);
4594
5075
  this.editorContainer.clear();
4595
5076
  this.editorContainer.addChild(dialog);
4596
5077
  this.ui.setFocus(dialog);
@@ -4827,7 +5308,9 @@ export class InteractiveMode {
4827
5308
  this.showStatus(`Session exported to: ${filePath}`);
4828
5309
  }
4829
5310
  else {
4830
- const filePath = await this.session.exportToHtml(outputPath);
5311
+ const filePath = await this.session.exportToHtml(outputPath, {
5312
+ themeName: theme.name,
5313
+ });
4831
5314
  this.showStatus(`Session exported to: ${filePath}`);
4832
5315
  }
4833
5316
  }
@@ -4903,89 +5386,74 @@ export class InteractiveMode {
4903
5386
  }
4904
5387
  }
4905
5388
  async handleShareCommand() {
4906
- // Check if gh is available and logged in
5389
+ // Check before confirmation so a user without a working GitHub CLI gets an actionable error.
4907
5390
  try {
4908
5391
  const authResult = spawnSync("gh", ["auth", "status"], { encoding: "utf-8" });
4909
5392
  if (authResult.status !== 0) {
4910
- this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");
5393
+ this.showError(formatShareUnavailableMessage("unauthenticated"));
4911
5394
  return;
4912
5395
  }
4913
5396
  }
4914
5397
  catch {
4915
- this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");
5398
+ this.showError(formatShareUnavailableMessage("missing"));
4916
5399
  return;
4917
5400
  }
4918
- // Export to a temp file
4919
- const tmpFile = path.join(os.tmpdir(), "session.html");
5401
+ let loader;
5402
+ let proc;
4920
5403
  try {
4921
- await this.session.exportToHtml(tmpFile);
4922
- }
4923
- catch (error) {
4924
- this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
4925
- return;
4926
- }
4927
- // Show cancellable loader, replacing the editor
4928
- const loader = new BorderedLoader(this.ui, theme, "Creating gist...");
4929
- this.editorContainer.clear();
4930
- this.editorContainer.addChild(loader);
4931
- this.ui.setFocus(loader);
4932
- this.ui.requestRender();
4933
- const restoreEditor = () => {
4934
- loader.dispose();
4935
- this.editorContainer.clear();
4936
- this.editorContainer.addChild(this.editor);
4937
- this.ui.setFocus(this.editor);
4938
- try {
4939
- fs.unlinkSync(tmpFile);
4940
- }
4941
- catch {
4942
- // Ignore cleanup errors
4943
- }
4944
- };
4945
- // Create a secret gist asynchronously
4946
- let proc = null;
4947
- loader.onAbort = () => {
4948
- proc?.kill();
4949
- restoreEditor();
4950
- this.showStatus("Share cancelled");
4951
- };
4952
- try {
4953
- const result = await new Promise((resolve) => {
4954
- proc = spawn("gh", ["gist", "create", "--public=false", tmpFile]);
4955
- let stdout = "";
4956
- let stderr = "";
4957
- proc.stdout?.on("data", (data) => {
4958
- stdout += data.toString();
4959
- });
4960
- proc.stderr?.on("data", (data) => {
4961
- stderr += data.toString();
4962
- });
4963
- proc.on("close", (code) => resolve({ stdout, stderr, code }));
5404
+ const result = await publishSessionShare({
5405
+ confirm: () => this.showExtensionConfirm("Publish session to GitHub?", "This uploads a complete HTML export to a secret GitHub Gist. Secret Gists are unlisted, not private or access-controlled; anyone with the URL can read the transcript, prompts, tool activity, and included file content. Review a local /export first if needed."),
5406
+ exportToHtml: async (exportPath) => {
5407
+ await this.session.exportToHtml(exportPath);
5408
+ },
5409
+ publishGist: (exportPath) => new Promise((resolve, reject) => {
5410
+ loader = new BorderedLoader(this.ui, theme, "Creating secret GitHub Gist...");
5411
+ this.editorContainer.clear();
5412
+ this.editorContainer.addChild(loader);
5413
+ this.ui.setFocus(loader);
5414
+ this.ui.requestRender();
5415
+ loader.onAbort = () => proc?.kill();
5416
+ proc = spawn("gh", ["gist", "create", "--public=false", exportPath]);
5417
+ let stdout = "";
5418
+ let stderr = "";
5419
+ proc.stdout?.on("data", (data) => {
5420
+ stdout += data.toString();
5421
+ });
5422
+ proc.stderr?.on("data", (data) => {
5423
+ stderr += data.toString();
5424
+ });
5425
+ proc.on("error", reject);
5426
+ proc.on("close", (code) => {
5427
+ if (loader?.signal.aborted)
5428
+ reject(new Error("Share cancelled"));
5429
+ else if (code !== 0)
5430
+ reject(new Error(stderr.trim() || "Unknown GitHub CLI error"));
5431
+ else
5432
+ resolve(stdout);
5433
+ });
5434
+ }),
4964
5435
  });
4965
- if (loader.signal.aborted)
4966
- return;
4967
- restoreEditor();
4968
- if (result.code !== 0) {
4969
- const errorMsg = result.stderr?.trim() || "Unknown error";
4970
- this.showError(`Failed to create gist: ${errorMsg}`);
4971
- return;
4972
- }
4973
- // Extract gist ID from the URL returned by gh
4974
- // gh returns something like: https://gist.github.com/username/GIST_ID
4975
- const gistUrl = result.stdout?.trim();
4976
- const gistId = gistUrl?.split("/").pop();
4977
- if (!gistId) {
4978
- this.showError("Failed to parse gist ID from gh output");
5436
+ if (result.kind === "cancelled") {
5437
+ this.showStatus("Share cancelled");
4979
5438
  return;
4980
5439
  }
4981
- // Create the preview URL
4982
- const previewUrl = getShareViewerUrl(gistId);
4983
- this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
5440
+ const previewUrl = getShareViewerUrl(result.gistId);
5441
+ this.showStatus(previewUrl
5442
+ ? `Secret GitHub Gist: ${result.gistUrl}\nConfigured preview: ${previewUrl}`
5443
+ : `Secret GitHub Gist: ${result.gistUrl}`);
4984
5444
  }
4985
5445
  catch (error) {
4986
- if (!loader.signal.aborted) {
4987
- restoreEditor();
4988
- this.showError(`Failed to create gist: ${error instanceof Error ? error.message : "Unknown error"}`);
5446
+ if (error instanceof Error && error.message === "Share cancelled")
5447
+ this.showStatus("Share cancelled");
5448
+ else
5449
+ this.showError(`Failed to create secret GitHub Gist: ${error instanceof Error ? error.message : "Unknown error"}`);
5450
+ }
5451
+ finally {
5452
+ if (loader) {
5453
+ loader.dispose();
5454
+ this.editorContainer.clear();
5455
+ this.editorContainer.addChild(this.editor);
5456
+ this.ui.setFocus(this.editor);
4989
5457
  }
4990
5458
  }
4991
5459
  }
@@ -5031,7 +5499,7 @@ export class InteractiveMode {
5031
5499
  this.chatContainer.addChild(new Text(theme.fg("dim", `Session name set: ${sessionName ?? name}`), 1, 0));
5032
5500
  this.ui.requestRender();
5033
5501
  }
5034
- handleSessionCommand() {
5502
+ async handleSessionCommand() {
5035
5503
  const stats = this.session.getSessionStats();
5036
5504
  const sessionName = this.sessionManager.getSessionName();
5037
5505
  const entries = this.sessionManager.getEntries();
@@ -5084,6 +5552,26 @@ export class InteractiveMode {
5084
5552
  : `\n${theme.fg("dim", "Cache Re-billed:")} ${detail}`;
5085
5553
  }
5086
5554
  }
5555
+ // Role and latency: the two dimensions the durable ledger (roadmap Phase 8)
5556
+ // carries that session entries never did. Scoped to this session's rows only
5557
+ // -- a ledger that also served other sessions must never leak their cost in.
5558
+ const usagePerformanceSamples = (await this.session.modelRuntime.listUsagePerformanceSamples()).filter((sample) => sample.sessionId === stats.sessionId);
5559
+ if (usagePerformanceSamples.length > 0) {
5560
+ const avgTtftMs = usagePerformanceSamples.reduce((sum, sample) => sum + sample.ttftMs, 0) / usagePerformanceSamples.length;
5561
+ const avgGenerationMs = usagePerformanceSamples.reduce((sum, sample) => sum + sample.generationMs, 0) /
5562
+ usagePerformanceSamples.length;
5563
+ info += `\n\n${theme.bold("Latency")}\n`;
5564
+ info += `${theme.fg("dim", "Avg TTFT:")} ${Math.round(avgTtftMs)}ms\n`;
5565
+ info += `${theme.fg("dim", "Avg Generation:")} ${Math.round(avgGenerationMs)}ms`;
5566
+ const roleRows = aggregateUsagePerformance(usagePerformanceSamples, "role");
5567
+ if (roleRows.length > 0) {
5568
+ info += `\n\n${theme.bold("Roles")}`;
5569
+ for (const row of roleRows) {
5570
+ const requestLabel = row.sampleCount === 1 ? "1 request" : `${row.sampleCount} requests`;
5571
+ info += `\n ${theme.fg("dim", `${row.key}:`)} $${row.cost.toFixed(3)} ${theme.fg("dim", `(${requestLabel})`)}`;
5572
+ }
5573
+ }
5574
+ }
5087
5575
  this.chatContainer.addChild(new Spacer(1));
5088
5576
  this.chatContainer.addChild(new Text(info, 1, 0));
5089
5577
  this.ui.requestRender();
@@ -5228,6 +5716,28 @@ export class InteractiveMode {
5228
5716
  this.chatContainer.addChild(new DynamicBorder());
5229
5717
  this.ui.requestRender();
5230
5718
  }
5719
+ /**
5720
+ * Renders what `collectSessionCommands` returns, so the list a user reads and the list
5721
+ * autocomplete offers are the same list rather than two that agree today. Unregistered
5722
+ * dispatch-only commands are outside that boundary; see the note there.
5723
+ */
5724
+ handleHelpCommand() {
5725
+ const commands = this.collectSessionCommands();
5726
+ const rows = commands
5727
+ .map((command) => {
5728
+ const invocation = command.argumentHint ? `/${command.name} ${command.argumentHint}` : `/${command.name}`;
5729
+ return `| \`${invocation}\` | ${command.description ?? ""} |`;
5730
+ })
5731
+ .join("\n");
5732
+ const help = `| Command | Description |\n|---------|-------------|\n${rows}\n\nType \`/\` to filter these, or \`/hotkeys\` for keyboard shortcuts.`;
5733
+ this.chatContainer.addChild(new Spacer(1));
5734
+ this.chatContainer.addChild(new DynamicBorder());
5735
+ this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Commands")), 1, 0));
5736
+ this.chatContainer.addChild(new Spacer(1));
5737
+ this.chatContainer.addChild(new Markdown(help, 1, 1, this.getMarkdownThemeWithSettings()));
5738
+ this.chatContainer.addChild(new DynamicBorder());
5739
+ this.ui.requestRender();
5740
+ }
5231
5741
  async handleClearCommand() {
5232
5742
  this.clearStatusIndicator();
5233
5743
  try {
@@ -5291,6 +5801,19 @@ export class InteractiveMode {
5291
5801
  }
5292
5802
  }
5293
5803
  async handleBashCommand(command, excludeFromContext = false) {
5804
+ if (this.isHandlingBashCommand) {
5805
+ this.showWarning("A bash command is already running; wait for it to finish");
5806
+ return;
5807
+ }
5808
+ this.isHandlingBashCommand = true;
5809
+ try {
5810
+ await this.runBashCommand(command, excludeFromContext);
5811
+ }
5812
+ finally {
5813
+ this.isHandlingBashCommand = false;
5814
+ }
5815
+ }
5816
+ async runBashCommand(command, excludeFromContext = false) {
5294
5817
  const extensionRunner = this.session.extensionRunner;
5295
5818
  // Emit user_bash event to let extensions intercept
5296
5819
  const eventResult = await extensionRunner.emitUserBash({
@@ -5364,7 +5887,7 @@ export class InteractiveMode {
5364
5887
  // Ignore, will be emitted as an event
5365
5888
  }
5366
5889
  }
5367
- stop() {
5890
+ stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
5368
5891
  this.disposeActiveSelector();
5369
5892
  if (this.settingsManager.getShowTerminalProgress()) {
5370
5893
  this.ui.terminal.setProgress(false);
@@ -5378,7 +5901,7 @@ export class InteractiveMode {
5378
5901
  this.unsubscribe();
5379
5902
  }
5380
5903
  if (this.isInitialized) {
5381
- this.stopInteractiveTui();
5904
+ this.stopInteractiveTui(fullscreenExitOutput);
5382
5905
  this.isInitialized = false;
5383
5906
  }
5384
5907
  this.unregisterSignalHandlers();