apex-code 0.0.1-alpha.0 → 0.0.1-alpha.10

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 (739) hide show
  1. package/CHANGELOG.md +471 -0
  2. package/README.md +74 -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 +35 -20
  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 +121 -9
  40. package/dist/core/agent-session.d.ts.map +1 -1
  41. package/dist/core/agent-session.js +364 -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 +11 -10
  49. package/dist/core/bash-executor.js.map +1 -1
  50. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  51. package/dist/core/compaction/branch-summarization.js +3 -0
  52. package/dist/core/compaction/branch-summarization.js.map +1 -1
  53. package/dist/core/compaction/compaction.d.ts +4 -3
  54. package/dist/core/compaction/compaction.d.ts.map +1 -1
  55. package/dist/core/compaction/compaction.js +41 -32
  56. package/dist/core/compaction/compaction.js.map +1 -1
  57. package/dist/core/context/deferred-schemas.d.ts +92 -0
  58. package/dist/core/context/deferred-schemas.d.ts.map +1 -0
  59. package/dist/core/context/deferred-schemas.js +74 -0
  60. package/dist/core/context/deferred-schemas.js.map +1 -0
  61. package/dist/core/context/eviction.d.ts +47 -0
  62. package/dist/core/context/eviction.d.ts.map +1 -0
  63. package/dist/core/context/eviction.js +83 -0
  64. package/dist/core/context/eviction.js.map +1 -0
  65. package/dist/core/context/pipeline.d.ts +90 -0
  66. package/dist/core/context/pipeline.d.ts.map +1 -0
  67. package/dist/core/context/pipeline.js +109 -0
  68. package/dist/core/context/pipeline.js.map +1 -0
  69. package/dist/core/credential-failover.d.ts +30 -0
  70. package/dist/core/credential-failover.d.ts.map +1 -0
  71. package/dist/core/credential-failover.js +46 -0
  72. package/dist/core/credential-failover.js.map +1 -0
  73. package/dist/core/credential-pool.d.ts +60 -0
  74. package/dist/core/credential-pool.d.ts.map +1 -0
  75. package/dist/core/credential-pool.js +84 -0
  76. package/dist/core/credential-pool.js.map +1 -0
  77. package/dist/core/defaults.d.ts +1 -0
  78. package/dist/core/defaults.d.ts.map +1 -1
  79. package/dist/core/defaults.js +9 -0
  80. package/dist/core/defaults.js.map +1 -1
  81. package/dist/core/delegation/agents.d.ts +15 -0
  82. package/dist/core/delegation/agents.d.ts.map +1 -0
  83. package/dist/core/delegation/agents.js +55 -0
  84. package/dist/core/delegation/agents.js.map +1 -0
  85. package/dist/core/delegation/ceiling.d.ts +29 -0
  86. package/dist/core/delegation/ceiling.d.ts.map +1 -0
  87. package/dist/core/delegation/ceiling.js +34 -0
  88. package/dist/core/delegation/ceiling.js.map +1 -0
  89. package/dist/core/delegation/runtime.d.ts +94 -0
  90. package/dist/core/delegation/runtime.d.ts.map +1 -0
  91. package/dist/core/delegation/runtime.js +117 -0
  92. package/dist/core/delegation/runtime.js.map +1 -0
  93. package/dist/core/durable-state/daemon.d.ts +37 -0
  94. package/dist/core/durable-state/daemon.d.ts.map +1 -0
  95. package/dist/core/durable-state/daemon.js +62 -0
  96. package/dist/core/durable-state/daemon.js.map +1 -0
  97. package/dist/core/durable-state/provenance.d.ts +7 -0
  98. package/dist/core/durable-state/provenance.d.ts.map +1 -0
  99. package/dist/core/durable-state/provenance.js +24 -0
  100. package/dist/core/durable-state/provenance.js.map +1 -0
  101. package/dist/core/durable-state/sqlite.d.ts +84 -0
  102. package/dist/core/durable-state/sqlite.d.ts.map +1 -0
  103. package/dist/core/durable-state/sqlite.js +355 -0
  104. package/dist/core/durable-state/sqlite.js.map +1 -0
  105. package/dist/core/environment.d.ts +12 -0
  106. package/dist/core/environment.d.ts.map +1 -0
  107. package/dist/core/environment.js +69 -0
  108. package/dist/core/environment.js.map +1 -0
  109. package/dist/core/evidence-policy.d.ts +17 -0
  110. package/dist/core/evidence-policy.d.ts.map +1 -0
  111. package/dist/core/evidence-policy.js +25 -0
  112. package/dist/core/evidence-policy.js.map +1 -0
  113. package/dist/core/evidence.d.ts +16 -0
  114. package/dist/core/evidence.d.ts.map +1 -0
  115. package/dist/core/evidence.js +51 -0
  116. package/dist/core/evidence.js.map +1 -0
  117. package/dist/core/experimental.d.ts +4 -0
  118. package/dist/core/experimental.d.ts.map +1 -1
  119. package/dist/core/experimental.js +6 -1
  120. package/dist/core/experimental.js.map +1 -1
  121. package/dist/core/extensions/index.d.ts +2 -2
  122. package/dist/core/extensions/index.d.ts.map +1 -1
  123. package/dist/core/extensions/index.js +1 -1
  124. package/dist/core/extensions/index.js.map +1 -1
  125. package/dist/core/extensions/loader.d.ts.map +1 -1
  126. package/dist/core/extensions/loader.js +115 -48
  127. package/dist/core/extensions/loader.js.map +1 -1
  128. package/dist/core/extensions/types.d.ts +44 -9
  129. package/dist/core/extensions/types.d.ts.map +1 -1
  130. package/dist/core/extensions/types.js +3 -0
  131. package/dist/core/extensions/types.js.map +1 -1
  132. package/dist/core/footer-data-provider.d.ts +1 -1
  133. package/dist/core/footer-data-provider.d.ts.map +1 -1
  134. package/dist/core/footer-data-provider.js +1 -1
  135. package/dist/core/footer-data-provider.js.map +1 -1
  136. package/dist/core/http-dispatcher.d.ts +1 -2
  137. package/dist/core/http-dispatcher.d.ts.map +1 -1
  138. package/dist/core/http-dispatcher.js +2 -17
  139. package/dist/core/http-dispatcher.js.map +1 -1
  140. package/dist/core/http-idle-timeout.d.ts +12 -0
  141. package/dist/core/http-idle-timeout.d.ts.map +1 -0
  142. package/dist/core/http-idle-timeout.js +27 -0
  143. package/dist/core/http-idle-timeout.js.map +1 -0
  144. package/dist/core/index.d.ts +1 -0
  145. package/dist/core/index.d.ts.map +1 -1
  146. package/dist/core/index.js +1 -0
  147. package/dist/core/index.js.map +1 -1
  148. package/dist/core/keybindings.d.ts +38 -13
  149. package/dist/core/keybindings.d.ts.map +1 -1
  150. package/dist/core/keybindings.js +26 -5
  151. package/dist/core/keybindings.js.map +1 -1
  152. package/dist/core/lsp/client.d.ts +78 -0
  153. package/dist/core/lsp/client.d.ts.map +1 -0
  154. package/dist/core/lsp/client.js +494 -0
  155. package/dist/core/lsp/client.js.map +1 -0
  156. package/dist/core/lsp/diagnostics.d.ts +47 -0
  157. package/dist/core/lsp/diagnostics.d.ts.map +1 -0
  158. package/dist/core/lsp/diagnostics.js +224 -0
  159. package/dist/core/lsp/diagnostics.js.map +1 -0
  160. package/dist/core/lsp/pool.d.ts +48 -0
  161. package/dist/core/lsp/pool.d.ts.map +1 -0
  162. package/dist/core/lsp/pool.js +150 -0
  163. package/dist/core/lsp/pool.js.map +1 -0
  164. package/dist/core/lsp/registry.d.ts +49 -0
  165. package/dist/core/lsp/registry.d.ts.map +1 -0
  166. package/dist/core/lsp/registry.js +215 -0
  167. package/dist/core/lsp/registry.js.map +1 -0
  168. package/dist/core/lsp/status.d.ts +23 -0
  169. package/dist/core/lsp/status.d.ts.map +1 -0
  170. package/dist/core/lsp/status.js +35 -0
  171. package/dist/core/lsp/status.js.map +1 -0
  172. package/dist/core/lsp/sync.d.ts +13 -0
  173. package/dist/core/lsp/sync.d.ts.map +1 -0
  174. package/dist/core/lsp/sync.js +71 -0
  175. package/dist/core/lsp/sync.js.map +1 -0
  176. package/dist/core/model-config.d.ts +18 -0
  177. package/dist/core/model-config.d.ts.map +1 -1
  178. package/dist/core/model-config.js +25 -6
  179. package/dist/core/model-config.js.map +1 -1
  180. package/dist/core/model-resolver.d.ts +21 -0
  181. package/dist/core/model-resolver.d.ts.map +1 -1
  182. package/dist/core/model-resolver.js +73 -8
  183. package/dist/core/model-resolver.js.map +1 -1
  184. package/dist/core/model-runtime.d.ts +73 -0
  185. package/dist/core/model-runtime.d.ts.map +1 -1
  186. package/dist/core/model-runtime.js +225 -8
  187. package/dist/core/model-runtime.js.map +1 -1
  188. package/dist/core/models-store.d.ts.map +1 -1
  189. package/dist/core/models-store.js +2 -1
  190. package/dist/core/models-store.js.map +1 -1
  191. package/dist/core/observability/aggregate.d.ts +21 -0
  192. package/dist/core/observability/aggregate.d.ts.map +1 -0
  193. package/dist/core/observability/aggregate.js +36 -0
  194. package/dist/core/observability/aggregate.js.map +1 -0
  195. package/dist/core/observability/otlp.d.ts +66 -0
  196. package/dist/core/observability/otlp.d.ts.map +1 -0
  197. package/dist/core/observability/otlp.js +95 -0
  198. package/dist/core/observability/otlp.js.map +1 -0
  199. package/dist/core/package-manager.d.ts.map +1 -1
  200. package/dist/core/package-manager.js +40 -16
  201. package/dist/core/package-manager.js.map +1 -1
  202. package/dist/core/permissions/gate.d.ts +42 -0
  203. package/dist/core/permissions/gate.d.ts.map +1 -0
  204. package/dist/core/permissions/gate.js +72 -0
  205. package/dist/core/permissions/gate.js.map +1 -0
  206. package/dist/core/permissions/modes.d.ts +18 -0
  207. package/dist/core/permissions/modes.d.ts.map +1 -0
  208. package/dist/core/permissions/modes.js +57 -0
  209. package/dist/core/permissions/modes.js.map +1 -0
  210. package/dist/core/permissions/responder.d.ts +30 -0
  211. package/dist/core/permissions/responder.d.ts.map +1 -0
  212. package/dist/core/permissions/responder.js +32 -0
  213. package/dist/core/permissions/responder.js.map +1 -0
  214. package/dist/core/permissions/rules.d.ts +34 -0
  215. package/dist/core/permissions/rules.d.ts.map +1 -0
  216. package/dist/core/permissions/rules.js +74 -0
  217. package/dist/core/permissions/rules.js.map +1 -0
  218. package/dist/core/permissions/startup.d.ts +45 -0
  219. package/dist/core/permissions/startup.d.ts.map +1 -0
  220. package/dist/core/permissions/startup.js +63 -0
  221. package/dist/core/permissions/startup.js.map +1 -0
  222. package/dist/core/permissions/store.d.ts +114 -0
  223. package/dist/core/permissions/store.d.ts.map +1 -0
  224. package/dist/core/permissions/store.js +229 -0
  225. package/dist/core/permissions/store.js.map +1 -0
  226. package/dist/core/pi-manifest.d.ts.map +1 -1
  227. package/dist/core/pi-manifest.js +2 -1
  228. package/dist/core/pi-manifest.js.map +1 -1
  229. package/dist/core/project-trust.d.ts.map +1 -1
  230. package/dist/core/project-trust.js +1 -1
  231. package/dist/core/project-trust.js.map +1 -1
  232. package/dist/core/provider-attribution.d.ts.map +1 -1
  233. package/dist/core/provider-attribution.js +6 -7
  234. package/dist/core/provider-attribution.js.map +1 -1
  235. package/dist/core/remote-catalog-provider.d.ts +2 -2
  236. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  237. package/dist/core/remote-catalog-provider.js +22 -17
  238. package/dist/core/remote-catalog-provider.js.map +1 -1
  239. package/dist/core/resource-loader.d.ts.map +1 -1
  240. package/dist/core/resource-loader.js +3 -2
  241. package/dist/core/resource-loader.js.map +1 -1
  242. package/dist/core/sandbox/bwrap-arguments.d.ts +52 -0
  243. package/dist/core/sandbox/bwrap-arguments.d.ts.map +1 -0
  244. package/dist/core/sandbox/bwrap-arguments.js +86 -0
  245. package/dist/core/sandbox/bwrap-arguments.js.map +1 -0
  246. package/dist/core/sandbox/child-entry.d.ts +2 -0
  247. package/dist/core/sandbox/child-entry.d.ts.map +1 -0
  248. package/dist/core/sandbox/child-entry.js +22 -0
  249. package/dist/core/sandbox/child-entry.js.map +1 -0
  250. package/dist/core/sandbox/cli-launch.d.ts +88 -0
  251. package/dist/core/sandbox/cli-launch.d.ts.map +1 -0
  252. package/dist/core/sandbox/cli-launch.js +272 -0
  253. package/dist/core/sandbox/cli-launch.js.map +1 -0
  254. package/dist/core/sandbox/cli-supervisor.d.ts +30 -0
  255. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -0
  256. package/dist/core/sandbox/cli-supervisor.js +123 -0
  257. package/dist/core/sandbox/cli-supervisor.js.map +1 -0
  258. package/dist/core/sandbox/default-hosts.d.ts +28 -0
  259. package/dist/core/sandbox/default-hosts.d.ts.map +1 -0
  260. package/dist/core/sandbox/default-hosts.js +69 -0
  261. package/dist/core/sandbox/default-hosts.js.map +1 -0
  262. package/dist/core/sandbox/full-access.d.ts +28 -0
  263. package/dist/core/sandbox/full-access.d.ts.map +1 -0
  264. package/dist/core/sandbox/full-access.js +63 -0
  265. package/dist/core/sandbox/full-access.js.map +1 -0
  266. package/dist/core/sandbox/git-identity.d.ts +57 -0
  267. package/dist/core/sandbox/git-identity.d.ts.map +1 -0
  268. package/dist/core/sandbox/git-identity.js +78 -0
  269. package/dist/core/sandbox/git-identity.js.map +1 -0
  270. package/dist/core/sandbox/host-approval.d.ts +42 -0
  271. package/dist/core/sandbox/host-approval.d.ts.map +1 -0
  272. package/dist/core/sandbox/host-approval.js +99 -0
  273. package/dist/core/sandbox/host-approval.js.map +1 -0
  274. package/dist/core/sandbox/linux-backend.d.ts +46 -0
  275. package/dist/core/sandbox/linux-backend.d.ts.map +1 -0
  276. package/dist/core/sandbox/linux-backend.js +304 -0
  277. package/dist/core/sandbox/linux-backend.js.map +1 -0
  278. package/dist/core/sandbox/macos-backend.d.ts +28 -0
  279. package/dist/core/sandbox/macos-backend.d.ts.map +1 -0
  280. package/dist/core/sandbox/macos-backend.js +313 -0
  281. package/dist/core/sandbox/macos-backend.js.map +1 -0
  282. package/dist/core/sandbox/network-proxy.d.ts +38 -0
  283. package/dist/core/sandbox/network-proxy.d.ts.map +1 -0
  284. package/dist/core/sandbox/network-proxy.js +153 -0
  285. package/dist/core/sandbox/network-proxy.js.map +1 -0
  286. package/dist/core/sandbox/network-refusal.d.ts +24 -0
  287. package/dist/core/sandbox/network-refusal.d.ts.map +1 -0
  288. package/dist/core/sandbox/network-refusal.js +72 -0
  289. package/dist/core/sandbox/network-refusal.js.map +1 -0
  290. package/dist/core/sandbox/policy.d.ts +40 -0
  291. package/dist/core/sandbox/policy.d.ts.map +1 -0
  292. package/dist/core/sandbox/policy.js +55 -0
  293. package/dist/core/sandbox/policy.js.map +1 -0
  294. package/dist/core/sandbox/profiles.d.ts +25 -0
  295. package/dist/core/sandbox/profiles.d.ts.map +1 -0
  296. package/dist/core/sandbox/profiles.js +23 -0
  297. package/dist/core/sandbox/profiles.js.map +1 -0
  298. package/dist/core/sandbox/rpc/command-client.d.ts +28 -0
  299. package/dist/core/sandbox/rpc/command-client.d.ts.map +1 -0
  300. package/dist/core/sandbox/rpc/command-client.js +86 -0
  301. package/dist/core/sandbox/rpc/command-client.js.map +1 -0
  302. package/dist/core/sandbox/rpc/command-proxy.d.ts +48 -0
  303. package/dist/core/sandbox/rpc/command-proxy.d.ts.map +1 -0
  304. package/dist/core/sandbox/rpc/command-proxy.js +120 -0
  305. package/dist/core/sandbox/rpc/command-proxy.js.map +1 -0
  306. package/dist/core/sandbox/rpc/credential-client.d.ts +20 -0
  307. package/dist/core/sandbox/rpc/credential-client.d.ts.map +1 -0
  308. package/dist/core/sandbox/rpc/credential-client.js +170 -0
  309. package/dist/core/sandbox/rpc/credential-client.js.map +1 -0
  310. package/dist/core/sandbox/rpc/credential-proxy.d.ts +44 -0
  311. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -0
  312. package/dist/core/sandbox/rpc/credential-proxy.js +287 -0
  313. package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -0
  314. package/dist/core/sandbox/rpc/framing.d.ts +35 -0
  315. package/dist/core/sandbox/rpc/framing.d.ts.map +1 -0
  316. package/dist/core/sandbox/rpc/framing.js +114 -0
  317. package/dist/core/sandbox/rpc/framing.js.map +1 -0
  318. package/dist/core/sandbox/rpc/git-credential-helper.d.ts +40 -0
  319. package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +1 -0
  320. package/dist/core/sandbox/rpc/git-credential-helper.js +152 -0
  321. package/dist/core/sandbox/rpc/git-credential-helper.js.map +1 -0
  322. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +48 -0
  323. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +1 -0
  324. package/dist/core/sandbox/rpc/git-credential-proxy.js +142 -0
  325. package/dist/core/sandbox/rpc/git-credential-proxy.js.map +1 -0
  326. package/dist/core/sandbox/supervisor-temp.d.ts +18 -0
  327. package/dist/core/sandbox/supervisor-temp.d.ts.map +1 -0
  328. package/dist/core/sandbox/supervisor-temp.js +21 -0
  329. package/dist/core/sandbox/supervisor-temp.js.map +1 -0
  330. package/dist/core/sandbox/supervisor.d.ts +51 -0
  331. package/dist/core/sandbox/supervisor.d.ts.map +1 -0
  332. package/dist/core/sandbox/supervisor.js +18 -0
  333. package/dist/core/sandbox/supervisor.js.map +1 -0
  334. package/dist/core/sandbox/terminal-handoff.d.ts +45 -0
  335. package/dist/core/sandbox/terminal-handoff.d.ts.map +1 -0
  336. package/dist/core/sandbox/terminal-handoff.js +176 -0
  337. package/dist/core/sandbox/terminal-handoff.js.map +1 -0
  338. package/dist/core/sandbox/terminal-size.d.ts +40 -0
  339. package/dist/core/sandbox/terminal-size.d.ts.map +1 -0
  340. package/dist/core/sandbox/terminal-size.js +105 -0
  341. package/dist/core/sandbox/terminal-size.js.map +1 -0
  342. package/dist/core/sandbox/violations.d.ts +23 -0
  343. package/dist/core/sandbox/violations.d.ts.map +1 -0
  344. package/dist/core/sandbox/violations.js +30 -0
  345. package/dist/core/sandbox/violations.js.map +1 -0
  346. package/dist/core/sdk.d.ts +32 -41
  347. package/dist/core/sdk.d.ts.map +1 -1
  348. package/dist/core/sdk.js +161 -6
  349. package/dist/core/sdk.js.map +1 -1
  350. package/dist/core/session-export.d.ts +4 -0
  351. package/dist/core/session-export.d.ts.map +1 -0
  352. package/dist/core/session-export.js +32 -0
  353. package/dist/core/session-export.js.map +1 -0
  354. package/dist/core/session-lease.d.ts +31 -0
  355. package/dist/core/session-lease.d.ts.map +1 -0
  356. package/dist/core/session-lease.js +138 -0
  357. package/dist/core/session-lease.js.map +1 -0
  358. package/dist/core/session-manager.d.ts +46 -2
  359. package/dist/core/session-manager.d.ts.map +1 -1
  360. package/dist/core/session-manager.js +62 -4
  361. package/dist/core/session-manager.js.map +1 -1
  362. package/dist/core/session-share.d.ts +31 -0
  363. package/dist/core/session-share.d.ts.map +1 -0
  364. package/dist/core/session-share.js +45 -0
  365. package/dist/core/session-share.js.map +1 -0
  366. package/dist/core/settings-diagnostics.d.ts +9 -0
  367. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  368. package/dist/core/settings-diagnostics.js +21 -0
  369. package/dist/core/settings-diagnostics.js.map +1 -0
  370. package/dist/core/settings-manager.d.ts +92 -9
  371. package/dist/core/settings-manager.d.ts.map +1 -1
  372. package/dist/core/settings-manager.js +138 -36
  373. package/dist/core/settings-manager.js.map +1 -1
  374. package/dist/core/skills.d.ts +19 -7
  375. package/dist/core/skills.d.ts.map +1 -1
  376. package/dist/core/skills.js +128 -52
  377. package/dist/core/skills.js.map +1 -1
  378. package/dist/core/slash-commands.d.ts.map +1 -1
  379. package/dist/core/slash-commands.js +5 -2
  380. package/dist/core/slash-commands.js.map +1 -1
  381. package/dist/core/system-prompt.d.ts +2 -2
  382. package/dist/core/system-prompt.d.ts.map +1 -1
  383. package/dist/core/system-prompt.js +59 -68
  384. package/dist/core/system-prompt.js.map +1 -1
  385. package/dist/core/timings.d.ts +1 -1
  386. package/dist/core/timings.d.ts.map +1 -1
  387. package/dist/core/timings.js +3 -2
  388. package/dist/core/timings.js.map +1 -1
  389. package/dist/core/tools/ask-user.d.ts +25 -0
  390. package/dist/core/tools/ask-user.d.ts.map +1 -0
  391. package/dist/core/tools/ask-user.js +49 -0
  392. package/dist/core/tools/ask-user.js.map +1 -0
  393. package/dist/core/tools/bash-command-segments.d.ts +22 -0
  394. package/dist/core/tools/bash-command-segments.d.ts.map +1 -0
  395. package/dist/core/tools/bash-command-segments.js +128 -0
  396. package/dist/core/tools/bash-command-segments.js.map +1 -0
  397. package/dist/core/tools/bash.d.ts +40 -4
  398. package/dist/core/tools/bash.d.ts.map +1 -1
  399. package/dist/core/tools/bash.js +183 -32
  400. package/dist/core/tools/bash.js.map +1 -1
  401. package/dist/core/tools/contract.d.ts +183 -0
  402. package/dist/core/tools/contract.d.ts.map +1 -0
  403. package/dist/core/tools/contract.js +48 -0
  404. package/dist/core/tools/contract.js.map +1 -0
  405. package/dist/core/tools/delegate.d.ts +38 -0
  406. package/dist/core/tools/delegate.d.ts.map +1 -0
  407. package/dist/core/tools/delegate.js +90 -0
  408. package/dist/core/tools/delegate.js.map +1 -0
  409. package/dist/core/tools/diagnostics.d.ts +39 -0
  410. package/dist/core/tools/diagnostics.d.ts.map +1 -0
  411. package/dist/core/tools/diagnostics.js +62 -0
  412. package/dist/core/tools/diagnostics.js.map +1 -0
  413. package/dist/core/tools/edit-diff.d.ts +0 -5
  414. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  415. package/dist/core/tools/edit-diff.js +2 -5
  416. package/dist/core/tools/edit-diff.js.map +1 -1
  417. package/dist/core/tools/edit.d.ts +12 -2
  418. package/dist/core/tools/edit.d.ts.map +1 -1
  419. package/dist/core/tools/edit.js +61 -10
  420. package/dist/core/tools/edit.js.map +1 -1
  421. package/dist/core/tools/find.d.ts +2 -2
  422. package/dist/core/tools/find.d.ts.map +1 -1
  423. package/dist/core/tools/find.js +13 -1
  424. package/dist/core/tools/find.js.map +1 -1
  425. package/dist/core/tools/grep.d.ts +4 -2
  426. package/dist/core/tools/grep.d.ts.map +1 -1
  427. package/dist/core/tools/grep.js +47 -10
  428. package/dist/core/tools/grep.js.map +1 -1
  429. package/dist/core/tools/index.d.ts +51 -6
  430. package/dist/core/tools/index.d.ts.map +1 -1
  431. package/dist/core/tools/index.js +145 -2
  432. package/dist/core/tools/index.js.map +1 -1
  433. package/dist/core/tools/ls.d.ts +4 -2
  434. package/dist/core/tools/ls.d.ts.map +1 -1
  435. package/dist/core/tools/ls.js +17 -1
  436. package/dist/core/tools/ls.js.map +1 -1
  437. package/dist/core/tools/lsp.d.ts +62 -0
  438. package/dist/core/tools/lsp.d.ts.map +1 -0
  439. package/dist/core/tools/lsp.js +216 -0
  440. package/dist/core/tools/lsp.js.map +1 -0
  441. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  442. package/dist/core/tools/output-accumulator.js +1 -1
  443. package/dist/core/tools/output-accumulator.js.map +1 -1
  444. package/dist/core/tools/path-permission.d.ts +18 -0
  445. package/dist/core/tools/path-permission.d.ts.map +1 -0
  446. package/dist/core/tools/path-permission.js +28 -0
  447. package/dist/core/tools/path-permission.js.map +1 -0
  448. package/dist/core/tools/plan-present.d.ts +27 -0
  449. package/dist/core/tools/plan-present.d.ts.map +1 -0
  450. package/dist/core/tools/plan-present.js +55 -0
  451. package/dist/core/tools/plan-present.js.map +1 -0
  452. package/dist/core/tools/powershell.d.ts +16 -0
  453. package/dist/core/tools/powershell.d.ts.map +1 -0
  454. package/dist/core/tools/powershell.js +39 -0
  455. package/dist/core/tools/powershell.js.map +1 -0
  456. package/dist/core/tools/read.d.ts +2 -2
  457. package/dist/core/tools/read.d.ts.map +1 -1
  458. package/dist/core/tools/read.js +14 -0
  459. package/dist/core/tools/read.js.map +1 -1
  460. package/dist/core/tools/skill-search.d.ts +32 -0
  461. package/dist/core/tools/skill-search.d.ts.map +1 -0
  462. package/dist/core/tools/skill-search.js +56 -0
  463. package/dist/core/tools/skill-search.js.map +1 -0
  464. package/dist/core/tools/test.d.ts +34 -0
  465. package/dist/core/tools/test.d.ts.map +1 -0
  466. package/dist/core/tools/test.js +75 -0
  467. package/dist/core/tools/test.js.map +1 -0
  468. package/dist/core/tools/todo-write.d.ts +31 -0
  469. package/dist/core/tools/todo-write.d.ts.map +1 -0
  470. package/dist/core/tools/todo-write.js +51 -0
  471. package/dist/core/tools/todo-write.js.map +1 -0
  472. package/dist/core/tools/tool-definition-wrapper.d.ts +13 -2
  473. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  474. package/dist/core/tools/tool-definition-wrapper.js +11 -1
  475. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  476. package/dist/core/tools/tool-schema.d.ts +22 -0
  477. package/dist/core/tools/tool-schema.d.ts.map +1 -0
  478. package/dist/core/tools/tool-schema.js +45 -0
  479. package/dist/core/tools/tool-schema.js.map +1 -0
  480. package/dist/core/tools/web-fetch.d.ts +26 -0
  481. package/dist/core/tools/web-fetch.d.ts.map +1 -0
  482. package/dist/core/tools/web-fetch.js +62 -0
  483. package/dist/core/tools/web-fetch.js.map +1 -0
  484. package/dist/core/tools/web-search-exa.d.ts +33 -0
  485. package/dist/core/tools/web-search-exa.d.ts.map +1 -0
  486. package/dist/core/tools/web-search-exa.js +128 -0
  487. package/dist/core/tools/web-search-exa.js.map +1 -0
  488. package/dist/core/tools/web-search.d.ts +26 -0
  489. package/dist/core/tools/web-search.d.ts.map +1 -0
  490. package/dist/core/tools/web-search.js +53 -0
  491. package/dist/core/tools/web-search.js.map +1 -0
  492. package/dist/core/tools/write.d.ts +16 -2
  493. package/dist/core/tools/write.d.ts.map +1 -1
  494. package/dist/core/tools/write.js +48 -2
  495. package/dist/core/tools/write.js.map +1 -1
  496. package/dist/core/trust-manager.d.ts.map +1 -1
  497. package/dist/core/trust-manager.js +2 -1
  498. package/dist/core/trust-manager.js.map +1 -1
  499. package/dist/core/usage-performance-store.d.ts +56 -0
  500. package/dist/core/usage-performance-store.d.ts.map +1 -0
  501. package/dist/core/usage-performance-store.js +85 -0
  502. package/dist/core/usage-performance-store.js.map +1 -0
  503. package/dist/core/web-search-provider.d.ts +64 -0
  504. package/dist/core/web-search-provider.d.ts.map +1 -0
  505. package/dist/core/web-search-provider.js +149 -0
  506. package/dist/core/web-search-provider.js.map +1 -0
  507. package/dist/extensions/llama/index.d.ts.map +1 -1
  508. package/dist/extensions/llama/index.js +2 -0
  509. package/dist/extensions/llama/index.js.map +1 -1
  510. package/dist/extensions/llama/provider.d.ts.map +1 -1
  511. package/dist/extensions/llama/provider.js +6 -2
  512. package/dist/extensions/llama/provider.js.map +1 -1
  513. package/dist/index.d.ts +7 -6
  514. package/dist/index.d.ts.map +1 -1
  515. package/dist/index.js +5 -4
  516. package/dist/index.js.map +1 -1
  517. package/dist/main.d.ts +5 -0
  518. package/dist/main.d.ts.map +1 -1
  519. package/dist/main.js +87 -22
  520. package/dist/main.js.map +1 -1
  521. package/dist/migrations.d.ts.map +1 -1
  522. package/dist/migrations.js +6 -5
  523. package/dist/migrations.js.map +1 -1
  524. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  525. package/dist/modes/interactive/components/config-selector.js +9 -6
  526. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  527. package/dist/modes/interactive/components/custom-editor.d.ts +72 -1
  528. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  529. package/dist/modes/interactive/components/custom-editor.js +338 -1
  530. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  531. package/dist/modes/interactive/components/dynamic-border.d.ts +7 -1
  532. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  533. package/dist/modes/interactive/components/dynamic-border.js +11 -2
  534. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  535. package/dist/modes/interactive/components/extension-selector.d.ts +4 -0
  536. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  537. package/dist/modes/interactive/components/extension-selector.js +24 -6
  538. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  539. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -4
  540. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  541. package/dist/modes/interactive/components/first-time-setup.js +17 -41
  542. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  543. package/dist/modes/interactive/components/first-use-hints.d.ts +8 -0
  544. package/dist/modes/interactive/components/first-use-hints.d.ts.map +1 -0
  545. package/dist/modes/interactive/components/first-use-hints.js +22 -0
  546. package/dist/modes/interactive/components/first-use-hints.js.map +1 -0
  547. package/dist/modes/interactive/components/footer.d.ts +23 -1
  548. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  549. package/dist/modes/interactive/components/footer.js +182 -21
  550. package/dist/modes/interactive/components/footer.js.map +1 -1
  551. package/dist/modes/interactive/components/index.d.ts +2 -0
  552. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  553. package/dist/modes/interactive/components/index.js +2 -0
  554. package/dist/modes/interactive/components/index.js.map +1 -1
  555. package/dist/modes/interactive/components/model-selector.d.ts +31 -9
  556. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  557. package/dist/modes/interactive/components/model-selector.js +236 -96
  558. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  559. package/dist/modes/interactive/components/settings-selector.d.ts +25 -5
  560. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  561. package/dist/modes/interactive/components/settings-selector.js +226 -58
  562. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  563. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  564. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  565. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  566. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  567. package/dist/modes/interactive/components/splash-header.d.ts +66 -0
  568. package/dist/modes/interactive/components/splash-header.d.ts.map +1 -0
  569. package/dist/modes/interactive/components/splash-header.js +180 -0
  570. package/dist/modes/interactive/components/splash-header.js.map +1 -0
  571. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  572. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  573. package/dist/modes/interactive/components/thinking-selector.js +74 -17
  574. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  575. package/dist/modes/interactive/components/tool-execution.d.ts +16 -1
  576. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  577. package/dist/modes/interactive/components/tool-execution.js +113 -35
  578. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  579. package/dist/modes/interactive/components/tool-panel.d.ts +30 -0
  580. package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -0
  581. package/dist/modes/interactive/components/tool-panel.js +130 -0
  582. package/dist/modes/interactive/components/tool-panel.js.map +1 -0
  583. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  584. package/dist/modes/interactive/external-editor.js +60 -10
  585. package/dist/modes/interactive/external-editor.js.map +1 -1
  586. package/dist/modes/interactive/interactive-mode.d.ts +61 -5
  587. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  588. package/dist/modes/interactive/interactive-mode.js +642 -203
  589. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  590. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  591. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  592. package/dist/modes/interactive/model-catalog-refresh.js +34 -0
  593. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  594. package/dist/modes/interactive/theme/apex.json +93 -0
  595. package/dist/modes/interactive/theme/dark.json +2 -0
  596. package/dist/modes/interactive/theme/light.json +3 -1
  597. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  598. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  599. package/dist/modes/interactive/theme/theme-controller.js +25 -11
  600. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  601. package/dist/modes/interactive/theme/theme-schema.json +11 -3
  602. package/dist/modes/interactive/theme/theme.d.ts +14 -3
  603. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  604. package/dist/modes/interactive/theme/theme.js +65 -10
  605. package/dist/modes/interactive/theme/theme.js.map +1 -1
  606. package/dist/modes/json-event.d.ts +18 -12
  607. package/dist/modes/json-event.d.ts.map +1 -1
  608. package/dist/modes/json-event.js +22 -5
  609. package/dist/modes/json-event.js.map +1 -1
  610. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  611. package/dist/modes/rpc/rpc-mode.js +2 -1
  612. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  613. package/dist/package-manager-cli.d.ts +1 -0
  614. package/dist/package-manager-cli.d.ts.map +1 -1
  615. package/dist/package-manager-cli.js +211 -14
  616. package/dist/package-manager-cli.js.map +1 -1
  617. package/dist/rpc-entry.d.ts.map +1 -1
  618. package/dist/rpc-entry.js +2 -1
  619. package/dist/rpc-entry.js.map +1 -1
  620. package/dist/server/create-harness.d.ts +1 -1
  621. package/dist/server/create-harness.d.ts.map +1 -1
  622. package/dist/server/create-harness.js +8 -5
  623. package/dist/server/create-harness.js.map +1 -1
  624. package/dist/testing/replay/metrics.d.ts +24 -0
  625. package/dist/testing/replay/metrics.d.ts.map +1 -0
  626. package/dist/testing/replay/metrics.js +35 -0
  627. package/dist/testing/replay/metrics.js.map +1 -0
  628. package/dist/testing/replay/recorded-provider.d.ts +10 -0
  629. package/dist/testing/replay/recorded-provider.d.ts.map +1 -0
  630. package/dist/testing/replay/recorded-provider.js +108 -0
  631. package/dist/testing/replay/recorded-provider.js.map +1 -0
  632. package/dist/testing/replay/runner.d.ts +56 -0
  633. package/dist/testing/replay/runner.d.ts.map +1 -0
  634. package/dist/testing/replay/runner.js +406 -0
  635. package/dist/testing/replay/runner.js.map +1 -0
  636. package/dist/themes/apex-logo.d.ts +41 -0
  637. package/dist/themes/apex-logo.d.ts.map +1 -0
  638. package/dist/themes/apex-logo.js +35 -0
  639. package/dist/themes/apex-logo.js.map +1 -0
  640. package/dist/utils/apex-code-user-agent.d.ts +2 -0
  641. package/dist/utils/apex-code-user-agent.d.ts.map +1 -0
  642. package/dist/utils/apex-code-user-agent.js +5 -0
  643. package/dist/utils/apex-code-user-agent.js.map +1 -0
  644. package/dist/utils/changelog.d.ts +7 -2
  645. package/dist/utils/changelog.d.ts.map +1 -1
  646. package/dist/utils/changelog.js +82 -13
  647. package/dist/utils/changelog.js.map +1 -1
  648. package/dist/utils/clipboard-image.d.ts.map +1 -1
  649. package/dist/utils/clipboard-image.js +1 -1
  650. package/dist/utils/clipboard-image.js.map +1 -1
  651. package/dist/utils/frontmatter.d.ts.map +1 -1
  652. package/dist/utils/frontmatter.js +2 -1
  653. package/dist/utils/frontmatter.js.map +1 -1
  654. package/dist/utils/management-http.d.ts +5 -3
  655. package/dist/utils/management-http.d.ts.map +1 -1
  656. package/dist/utils/management-http.js +14 -10
  657. package/dist/utils/management-http.js.map +1 -1
  658. package/dist/utils/shell.d.ts +3 -0
  659. package/dist/utils/shell.d.ts.map +1 -1
  660. package/dist/utils/shell.js +17 -5
  661. package/dist/utils/shell.js.map +1 -1
  662. package/dist/utils/syntax-highlight.d.ts +1 -0
  663. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  664. package/dist/utils/syntax-highlight.js +60 -1
  665. package/dist/utils/syntax-highlight.js.map +1 -1
  666. package/dist/utils/text.d.ts +8 -0
  667. package/dist/utils/text.d.ts.map +1 -0
  668. package/dist/utils/text.js +9 -0
  669. package/dist/utils/text.js.map +1 -0
  670. package/dist/utils/tools-manager.d.ts +70 -2
  671. package/dist/utils/tools-manager.d.ts.map +1 -1
  672. package/dist/utils/tools-manager.js +280 -124
  673. package/dist/utils/tools-manager.js.map +1 -1
  674. package/dist/utils/version-check.d.ts +7 -5
  675. package/dist/utils/version-check.d.ts.map +1 -1
  676. package/dist/utils/version-check.js +25 -14
  677. package/dist/utils/version-check.js.map +1 -1
  678. package/dist/utils/windows-self-update.d.ts.map +1 -1
  679. package/dist/utils/windows-self-update.js +1 -1
  680. package/dist/utils/windows-self-update.js.map +1 -1
  681. package/docs/compaction.md +39 -24
  682. package/docs/containerization.md +24 -21
  683. package/docs/custom-provider.md +15 -12
  684. package/docs/development.md +11 -11
  685. package/docs/environment-variables.md +37 -77
  686. package/docs/extensions.md +68 -54
  687. package/docs/index.md +12 -24
  688. package/docs/json.md +18 -11
  689. package/docs/keybindings.md +18 -12
  690. package/docs/llama-cpp.md +9 -7
  691. package/docs/models.md +17 -11
  692. package/docs/packages.md +47 -43
  693. package/docs/prompt-templates.md +3 -3
  694. package/docs/providers.md +10 -10
  695. package/docs/quickstart.md +49 -32
  696. package/docs/rpc.md +35 -18
  697. package/docs/sdk.md +20 -6
  698. package/docs/security.md +18 -18
  699. package/docs/session-format.md +9 -9
  700. package/docs/sessions.md +13 -13
  701. package/docs/settings.md +83 -16
  702. package/docs/shell-aliases.md +1 -1
  703. package/docs/skills.md +66 -15
  704. package/docs/terminal-setup.md +44 -19
  705. package/docs/termux.md +4 -4
  706. package/docs/themes.md +32 -11
  707. package/docs/tmux.md +4 -4
  708. package/docs/tui.md +5 -5
  709. package/docs/usage.md +61 -53
  710. package/docs/windows.md +24 -2
  711. package/examples/extensions/border-status-editor.ts +1 -1
  712. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  713. package/examples/extensions/custom-provider-anthropic/package.json +2 -2
  714. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  715. package/examples/extensions/git-checkpoint.ts +2 -2
  716. package/examples/extensions/gondolin/package-lock.json +2 -2
  717. package/examples/extensions/gondolin/package.json +1 -1
  718. package/examples/extensions/notify.ts +3 -1
  719. package/examples/extensions/sandbox/package-lock.json +2 -2
  720. package/examples/extensions/sandbox/package.json +1 -1
  721. package/examples/extensions/subagent/README.md +3 -1
  722. package/examples/extensions/subagent/agents.ts +40 -9
  723. package/examples/extensions/subagent/index.ts +27 -4
  724. package/examples/extensions/titlebar-spinner.ts +1 -1
  725. package/examples/extensions/with-deps/package-lock.json +2 -2
  726. package/examples/extensions/with-deps/package.json +1 -1
  727. package/examples/rpc-extension-ui.ts +1 -1
  728. package/examples/sdk/06-extensions.ts +1 -1
  729. package/examples/sdk/README.md +1 -1
  730. package/npm-shrinkwrap.json +280 -445
  731. package/package.json +9 -12
  732. package/dist/core/telemetry.d.ts +0 -3
  733. package/dist/core/telemetry.d.ts.map +0 -1
  734. package/dist/core/telemetry.js +0 -9
  735. package/dist/core/telemetry.js.map +0 -1
  736. package/dist/utils/pi-user-agent.d.ts +0 -2
  737. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  738. package/dist/utils/pi-user-agent.js +0 -5
  739. 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,6 +314,7 @@ 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;
279
320
  // Track pending bash components (shown in pending area, moved to chat on submit)
@@ -323,6 +364,14 @@ export class InteractiveMode {
323
364
  get settingsManager() {
324
365
  return this.session.settingsManager;
325
366
  }
367
+ offerFirstUseHint(id) {
368
+ this.firstUseHints ??= new FirstUseHints(this.settingsManager.getFirstUseHints());
369
+ const hint = this.firstUseHints.offer(id);
370
+ if (!hint)
371
+ return;
372
+ this.settingsManager.setFirstUseHints(this.firstUseHints.getSeen());
373
+ this.chatContainer.addChild(new Text(theme.fg("dim", hint), this.outputPad, 0));
374
+ }
326
375
  constructor(runtimeHost, options = {}) {
327
376
  this.runtimeHost = runtimeHost;
328
377
  const tuiMode = options.tuiMode ?? this.settingsManager.getTuiMode();
@@ -333,6 +382,7 @@ export class InteractiveMode {
333
382
  });
334
383
  this.runtimeHost.setRebindSession(async () => {
335
384
  await this.rebindCurrentSession({ renderBeforeBind: true });
385
+ await this.themeController.applyFromSettings();
336
386
  });
337
387
  this.version = VERSION;
338
388
  this.renderer = createInteractiveTui({
@@ -361,12 +411,20 @@ export class InteractiveMode {
361
411
  this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
362
412
  paddingX: editorPaddingX,
363
413
  autocompleteMaxVisible,
414
+ promptPrefix: "› ",
415
+ promptColor: (text) => theme.fg("accent", text),
416
+ placeholder: "Ask anything",
417
+ placeholderColor: (text) => theme.fg("dim", text),
418
+ commandColor: (text) => theme.fg("accent", text),
419
+ surfaceColor: (text) => theme.bg("userMessageBg", text),
420
+ autocompleteRule: (width) => theme.fg("borderMuted", (this.settingsManager.getSymbolPreset() === "ascii" ? "-" : "┄").repeat(Math.max(1, width))),
421
+ autocompleteFooter: () => this.buildAutocompleteFooter(),
364
422
  });
365
423
  this.editor = this.defaultEditor;
366
424
  this.editorContainer = new Container();
367
425
  this.editorContainer.addChild(this.editor);
368
426
  this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
369
- this.footer = new FooterComponent(this.session, this.footerDataProvider);
427
+ this.footer = new FooterComponent(this.session, this.footerDataProvider, this.settingsManager);
370
428
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
371
429
  this.footerContainer = new Container();
372
430
  this.footerContainer.addChild(this.footer);
@@ -375,7 +433,12 @@ export class InteractiveMode {
375
433
  this.outputPad = this.settingsManager.getOutputPad();
376
434
  // Register themes from resource loader and initialize
377
435
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
378
- this.themeController = new InteractiveThemeController(this.ui, this.settingsManager, (message) => this.showError(message), () => this.updateEditorBorderColor());
436
+ this.themeController = new InteractiveThemeController(this.ui, {
437
+ getSettingsManager: () => this.settingsManager,
438
+ showError: (message) => this.showError(message),
439
+ onChanged: () => this.updateEditorBorderColor(),
440
+ initialThemeSetting: options.initialThemeSetting,
441
+ });
379
442
  }
380
443
  getAutocompleteSourceTag(sourceInfo) {
381
444
  if (!sourceInfo) {
@@ -445,6 +508,15 @@ export class InteractiveMode {
445
508
  }));
446
509
  };
447
510
  }
511
+ const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
512
+ if (thinkingCommand) {
513
+ thinkingCommand.getArgumentCompletions = (prefix) => {
514
+ return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
515
+ value: level,
516
+ label: level,
517
+ }));
518
+ };
519
+ }
448
520
  const loginCommand = slashCommands.find((command) => command.name === "login");
449
521
  if (loginCommand) {
450
522
  loginCommand.getArgumentCompletions = (prefix) => {
@@ -477,7 +549,7 @@ export class InteractiveMode {
477
549
  const skillCommandList = [];
478
550
  if (this.settingsManager.getEnableSkillCommands()) {
479
551
  for (const skill of this.session.resourceLoader.getSkills().skills) {
480
- const commandName = `skill:${skill.name}`;
552
+ const commandName = `skill:${slugifySkillCommandName(skill.name)}`;
481
553
  this.skillCommands.set(commandName, skill.filePath);
482
554
  skillCommandList.push({
483
555
  name: commandName,
@@ -516,7 +588,7 @@ export class InteractiveMode {
516
588
  }
517
589
  this.chatContainer.addChild(new DynamicBorder());
518
590
  if (this.settingsManager.getCollapseChangelog()) {
519
- const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
591
+ const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?)\]?/);
520
592
  const latestVersion = versionMatch ? versionMatch[1] : this.version;
521
593
  const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
522
594
  this.chatContainer.addChild(new Text(condensedText, 1, 0));
@@ -538,14 +610,14 @@ export class InteractiveMode {
538
610
  tui.setLayoutRoot(this.fullscreenLayoutRoot);
539
611
  }
540
612
  }
541
- stopInteractiveTui() {
542
- if (this.renderer.mode === "fullscreen") {
613
+ stopInteractiveTui(fullscreenExitOutput) {
614
+ if (this.renderer.mode === "fullscreen" && fullscreenExitOutput === "transcript") {
543
615
  while (this.renderer.hasOverlayEntries)
544
616
  this.renderer.hideOverlay();
545
617
  this.switchTuiMode("regular", false, false);
546
618
  this.renderer.renderNow();
547
619
  }
548
- this.ui.stop();
620
+ this.ui.stop({ preserveScreen: this.renderer.mode === "fullscreen" });
549
621
  }
550
622
  switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
551
623
  const previousUi = this.renderer;
@@ -602,10 +674,9 @@ export class InteractiveMode {
602
674
  this.registerSignalHandlers();
603
675
  // Load changelog (only show new entries, skip for resumed sessions)
604
676
  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;
677
+ // Resolve the effective mode before mounting or focusing components. Both
678
+ // operations can schedule a frame even before the renderer is started.
679
+ await this.refreshFooterPermissionMode();
609
680
  if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
610
681
  const modelList = this.session.scopedModels
611
682
  .map((sm) => {
@@ -649,18 +720,23 @@ export class InteractiveMode {
649
720
  this.widgetContainerBelow,
650
721
  this.footerContainer,
651
722
  ]);
723
+ // Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
724
+ this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
725
+ this.defaultEditor.onCtrlD = () => this.handleCtrlD();
726
+ this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
652
727
  this.ui.setFocus(this.editor);
653
- this.setupKeyHandlers();
654
- this.setupEditorSubmitHandler();
655
728
  // Start the UI before initializing extensions so session_start handlers can use interactive dialogs
656
729
  this.ui.start();
657
730
  this.isInitialized = true;
658
731
  await this.themeController.applyFromSettings();
659
- // Add header with keybindings from config (unless silenced)
732
+ // Brand splash: mark on the left, runtime metadata on the right. The model
733
+ // and cwd are read through live getters, so they fill in once the session
734
+ // binds in rebindCurrentSession below.
660
735
  if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
661
- const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
662
736
  // Build startup instructions using keybinding hint helpers
663
737
  const hint = (keybinding, description) => keyHint(keybinding, description);
738
+ // The full cheatsheet is verbose-only. The default screen leads with the
739
+ // mark and the runtime facts that actually differ between launches.
664
740
  const expandedInstructions = [
665
741
  hint("app.interrupt", "to interrupt"),
666
742
  hint("app.clear", "to clear"),
@@ -682,27 +758,45 @@ export class InteractiveMode {
682
758
  hint("app.clipboard.pasteImage", "to paste image (with text fallback)"),
683
759
  rawKeyHint("drop files", "to attach"),
684
760
  ].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);
761
+ this.builtInHeader = new ApexSplashHeader(this.version, () => this.session.state.model?.id, () => this.session.sessionManager.getCwd(), this.options.verbose ? expandedInstructions : undefined, {
762
+ topPadding: true,
763
+ getSymbolPreset: () => this.settingsManager.getSymbolPreset(),
764
+ getExtraMetadata: () => {
765
+ // Which branch the session is rooted on differs between launches
766
+ // and is not derivable from the prompt, so it belongs here. The
767
+ // tray carries it too, but drops it first when space is tight.
768
+ const branch = this.footerDataProvider.getGitBranch();
769
+ return branch ? [{ label: "branch", value: branch }] : [];
770
+ },
771
+ getInventory: () => this.buildStartupInventory(),
772
+ inventoryHint: "/resources",
773
+ getShortcuts: () => this.buildStartupShortcuts(),
774
+ getHint: () => "Apex Code can explain its own features and look up its docs.",
775
+ });
695
776
  // Setup UI layout
696
- this.headerContainer.addChild(new Spacer(1));
777
+ this.startupInventoryVisible = true;
697
778
  this.headerContainer.addChild(this.builtInHeader);
698
779
  this.headerContainer.addChild(new Spacer(1));
699
780
  }
700
781
  else {
701
782
  // Minimal header when silenced
783
+ this.startupInventoryVisible = false;
702
784
  this.builtInHeader = new Text("", 0, 0);
703
785
  this.headerContainer.addChild(this.builtInHeader);
704
786
  }
705
787
  this.ui.requestRender();
788
+ // Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
789
+ // so slow downloads do not make startup appear frozen.
790
+ // Both are needed: fd for autocomplete, rg for grep tool and bash commands.
791
+ const [fdPath] = await Promise.all([
792
+ ensureTool("fd", (status) => this.showManagedToolStatus(status)),
793
+ ensureTool("rg", (status) => this.showManagedToolStatus(status)),
794
+ ]);
795
+ this.fdPath = fdPath;
796
+ // Enable the remaining input handlers only after managed-tool setup completes.
797
+ this.setupKeyHandlers();
798
+ this.setupEditorSubmitHandler();
799
+ this.ui.requestRender();
706
800
  // Initialize extensions first so resources are shown before messages
707
801
  await this.rebindCurrentSession();
708
802
  // Render initial messages AFTER showing loaded resources
@@ -719,6 +813,14 @@ export class InteractiveMode {
719
813
  });
720
814
  // Initialize available provider count for footer display
721
815
  await this.updateAvailableProviderCount();
816
+ // Flush the completed startup state before loading the remaining syntax grammars.
817
+ this.ui.renderNow();
818
+ void loadAllHighlightLanguages().then(() => {
819
+ if (!this.isInitialized)
820
+ return;
821
+ this.ui.invalidate();
822
+ this.ui.requestRender();
823
+ });
722
824
  }
723
825
  /**
724
826
  * Update terminal title with session name and cwd.
@@ -739,17 +841,16 @@ export class InteractiveMode {
739
841
  */
740
842
  async run() {
741
843
  await this.init();
742
- if (!process.env.PI_OFFLINE) {
844
+ if (!getApexEnvironment("APEX_CODE_OFFLINE")) {
743
845
  const controller = new AbortController();
744
846
  const timeout = setTimeout(() => controller.abort(), 15_000);
745
- void this.session.modelRuntime
746
- .refresh({ signal: controller.signal })
847
+ void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
747
848
  .then(() => this.updateAvailableProviderCount())
748
849
  .catch(() => { })
749
850
  .finally(() => clearTimeout(timeout));
750
851
  }
751
852
  // Start version check asynchronously
752
- checkForNewPiVersion(this.version).then((newRelease) => {
853
+ checkForNewApexCodeVersion(this.version).then((newRelease) => {
753
854
  if (newRelease) {
754
855
  this.showNewVersionNotification(newRelease);
755
856
  }
@@ -775,7 +876,18 @@ export class InteractiveMode {
775
876
  }
776
877
  });
777
878
  // Show startup warnings
778
- const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
879
+ const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
880
+ for (const diagnostic of startupDiagnostics ?? []) {
881
+ if (diagnostic.type === "error") {
882
+ this.showError(diagnostic.message);
883
+ }
884
+ else if (diagnostic.type === "warning") {
885
+ this.showWarning(diagnostic.message);
886
+ }
887
+ else {
888
+ this.showStatus(diagnostic.message);
889
+ }
890
+ }
779
891
  if (migratedProviders && migratedProviders.length > 0) {
780
892
  this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
781
893
  }
@@ -821,7 +933,7 @@ export class InteractiveMode {
821
933
  }
822
934
  }
823
935
  async checkForPackageUpdates() {
824
- if (process.env.PI_OFFLINE) {
936
+ if (getApexEnvironment("APEX_CODE_OFFLINE")) {
825
937
  return [];
826
938
  }
827
939
  try {
@@ -874,7 +986,7 @@ export class InteractiveMode {
874
986
  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
987
  }
876
988
  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.";
989
+ 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
990
  }
879
991
  return undefined;
880
992
  }
@@ -891,35 +1003,17 @@ export class InteractiveMode {
891
1003
  const changelogPath = getChangelogPath();
892
1004
  const entries = parseChangelog(changelogPath);
893
1005
  if (!lastVersion) {
894
- // Fresh install - record the version, send telemetry, don't show changelog
1006
+ // Fresh install - record the version, don't show changelog
895
1007
  this.settingsManager.setLastChangelogVersion(VERSION);
896
- this.reportInstallTelemetry(VERSION);
897
1008
  return undefined;
898
1009
  }
899
1010
  const newEntries = getNewEntries(entries, lastVersion);
900
1011
  if (newEntries.length > 0) {
901
1012
  this.settingsManager.setLastChangelogVersion(VERSION);
902
- this.reportInstallTelemetry(VERSION);
903
1013
  return newEntries.map((e) => normalizeChangelogLinks(e.content, e)).join("\n\n");
904
1014
  }
905
1015
  return undefined;
906
1016
  }
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
1017
  getMarkdownThemeWithSettings() {
924
1018
  return {
925
1019
  ...getMarkdownTheme(),
@@ -1216,7 +1310,7 @@ export class InteractiveMode {
1216
1310
  showLoadedResources(options) {
1217
1311
  // Resource rendering is idempotent; chat clears no longer clear this separate container.
1218
1312
  this.loadedResourcesContainer.clear();
1219
- const showListing = options?.force || this.options.verbose || !this.settingsManager.getQuietStartup();
1313
+ const showListing = options?.force || this.options.verbose;
1220
1314
  const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
1221
1315
  if (!showListing && !showDiagnostics) {
1222
1316
  return;
@@ -1333,43 +1427,105 @@ export class InteractiveMode {
1333
1427
  addLoadedSection("Themes", themeCompactList, themeList);
1334
1428
  }
1335
1429
  }
1430
+ // Warnings collapse into the counted line when that line is on screen; the
1431
+ // full trace lives behind /resources. Errors always surface inline,
1432
+ // because an error means a resource did not load at all.
1336
1433
  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));
1434
+ const collapseWarnings = !showListing && this.startupInventoryVisible;
1435
+ for (const group of this.collectDiagnosticGroups()) {
1436
+ const visible = collapseWarnings
1437
+ ? group.diagnostics.filter((diagnostic) => diagnostic.type === "error")
1438
+ : group.diagnostics;
1439
+ if (visible.length === 0)
1440
+ continue;
1441
+ const severity = visible.some((diagnostic) => diagnostic.type === "error") ? "error" : "warning";
1442
+ const body = this.formatDiagnostics(visible, sourceInfos);
1443
+ this.loadedResourcesContainer.addChild(new Text(`${theme.fg(severity, `[${group.label}]`)}\n${body}`, 0, 0));
1370
1444
  this.loadedResourcesContainer.addChild(new Spacer(1));
1371
1445
  }
1372
1446
  }
1447
+ // Counts on the startup screen are read at render time, so they fill in
1448
+ // once resources finish loading without an explicit refresh here.
1449
+ this.builtInHeader?.invalidate?.();
1450
+ }
1451
+ /**
1452
+ * Every startup diagnostic, grouped by the resource kind that produced it.
1453
+ *
1454
+ * One list feeds both the counted line in the header and the detailed
1455
+ * listing, so the two can never disagree about how many issues exist.
1456
+ */
1457
+ collectDiagnosticGroups() {
1458
+ const extensionDiagnostics = [
1459
+ ...this.session.resourceLoader
1460
+ .getExtensions()
1461
+ .errors.map((error) => ({ type: "error", message: error.error, path: error.path })),
1462
+ ...this.session.extensionRunner.getCommandDiagnostics(),
1463
+ ...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner),
1464
+ ...this.session.extensionRunner.getShortcutDiagnostics(),
1465
+ ];
1466
+ return [
1467
+ { label: "Skill conflicts", diagnostics: this.session.resourceLoader.getSkills().diagnostics },
1468
+ { label: "Prompt conflicts", diagnostics: this.session.resourceLoader.getPrompts().diagnostics },
1469
+ { label: "Extension issues", diagnostics: extensionDiagnostics },
1470
+ { label: "Theme conflicts", diagnostics: this.session.resourceLoader.getThemes().diagnostics },
1471
+ ].filter((group) => group.diagnostics.length > 0);
1472
+ }
1473
+ /**
1474
+ * The counted line in the ruled band under the mark.
1475
+ *
1476
+ * The startup screen used to print all 152 skill names across fifteen rows.
1477
+ * A count answers the only question that screen can usefully answer, which
1478
+ * is what got loaded; the names live behind /resources now.
1479
+ */
1480
+ /**
1481
+ * The sigil row under the hint: how to reach commands, bash, and files.
1482
+ *
1483
+ * These are the only three characters that change what the composer does, so
1484
+ * they take the accent and everything around them stays quiet.
1485
+ */
1486
+ /** Live keybindings under the autocomplete dropdown. */
1487
+ buildAutocompleteFooter() {
1488
+ const separator = theme.fg("borderMuted", this.settingsManager.getSymbolPreset() === "ascii" ? " - " : " · ");
1489
+ return [
1490
+ rawKeyHint(`${keyText("tui.select.up")}/${keyText("tui.select.down")}`, "move"),
1491
+ keyHint("tui.select.confirm", "select"),
1492
+ keyHint("tui.input.tab", "complete"),
1493
+ keyHint("tui.select.cancel", "dismiss"),
1494
+ ].join(separator);
1495
+ }
1496
+ buildStartupShortcuts() {
1497
+ const entry = (sigil, label) => `${theme.fg("accent", sigil)} ${theme.fg("muted", label)}`;
1498
+ return [
1499
+ entry("/", "commands"),
1500
+ entry("!", "bash"),
1501
+ entry("@", "files"),
1502
+ `${theme.fg("dim", keyText("app.interrupt"))} ${theme.fg("muted", "interrupt")}`,
1503
+ ].join(" ");
1504
+ }
1505
+ buildStartupInventory() {
1506
+ const counted = (total, singular) => `${total} ${singular}${total === 1 ? "" : "s"}`;
1507
+ const parts = [];
1508
+ const skills = this.session.resourceLoader.getSkills().skills.length;
1509
+ const extensions = this.session.resourceLoader.getExtensions().extensions.filter((e) => !e.hidden).length;
1510
+ const prompts = this.session.resourceLoader.getPrompts().prompts.length;
1511
+ if (skills > 0)
1512
+ parts.push(theme.fg("muted", counted(skills, "skill")));
1513
+ if (extensions > 0)
1514
+ parts.push(theme.fg("muted", counted(extensions, "extension")));
1515
+ if (prompts > 0)
1516
+ parts.push(theme.fg("muted", counted(prompts, "prompt")));
1517
+ const diagnostics = this.collectDiagnosticGroups().flatMap((group) => group.diagnostics);
1518
+ const errors = diagnostics.filter((diagnostic) => diagnostic.type === "error").length;
1519
+ const conflicts = diagnostics.length - errors;
1520
+ if (errors > 0)
1521
+ parts.push(theme.fg("error", counted(errors, "error")));
1522
+ if (conflicts > 0)
1523
+ parts.push(theme.fg("warning", counted(conflicts, "conflict")));
1524
+ if (parts.length === 0)
1525
+ return undefined;
1526
+ const ascii = this.settingsManager.getSymbolPreset() === "ascii";
1527
+ const separator = theme.fg("borderMuted", ascii ? " - " : " · ");
1528
+ return parts.join(separator);
1373
1529
  }
1374
1530
  /**
1375
1531
  * Initialize the extension system with TUI-based UI context.
@@ -1457,6 +1613,7 @@ export class InteractiveMode {
1457
1613
  this.applyFullscreenScrollbarSetting();
1458
1614
  this.footer.setSession(this.session);
1459
1615
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
1616
+ void this.refreshFooterPermissionMode();
1460
1617
  this.footerDataProvider.setCwd(this.sessionManager.getCwd());
1461
1618
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
1462
1619
  this.outputPad = this.settingsManager.getOutputPad();
@@ -1499,7 +1656,7 @@ export class InteractiveMode {
1499
1656
  const message = error instanceof Error ? error.message : String(error);
1500
1657
  this.showError(`${prefix}: ${message}`);
1501
1658
  stopThemeWatcher();
1502
- this.stop();
1659
+ this.stop("transcript");
1503
1660
  process.exit(1);
1504
1661
  }
1505
1662
  renderCurrentSessionState() {
@@ -1509,9 +1666,14 @@ export class InteractiveMode {
1509
1666
  this.compactionQueuedMessages = [];
1510
1667
  this.streamingComponent = undefined;
1511
1668
  this.streamingMessage = undefined;
1512
- this.pendingTools.clear();
1669
+ this.clearPendingTools();
1513
1670
  this.renderInitialMessages();
1514
1671
  }
1672
+ clearPendingTools() {
1673
+ for (const component of this.pendingTools.values())
1674
+ component.dispose();
1675
+ this.pendingTools.clear();
1676
+ }
1515
1677
  /**
1516
1678
  * Get a registered tool definition by name (for custom rendering).
1517
1679
  */
@@ -1894,7 +2056,7 @@ export class InteractiveMode {
1894
2056
  /**
1895
2057
  * Show a selector for extensions.
1896
2058
  */
1897
- showExtensionSelector(title, options, opts) {
2059
+ showExtensionSelector(title, options, opts, enableSearch = false) {
1898
2060
  return new Promise((resolve) => {
1899
2061
  if (opts?.signal?.aborted) {
1900
2062
  resolve(undefined);
@@ -1913,7 +2075,12 @@ export class InteractiveMode {
1913
2075
  opts?.signal?.removeEventListener("abort", onAbort);
1914
2076
  this.hideExtensionSelector();
1915
2077
  resolve(undefined);
1916
- }, { tui: this.ui, timeout: opts?.timeout, onToggleToolsExpanded: () => this.toggleToolOutputExpansion() });
2078
+ }, {
2079
+ tui: this.ui,
2080
+ timeout: opts?.timeout,
2081
+ onToggleToolsExpanded: () => this.toggleToolOutputExpansion(),
2082
+ enableSearch,
2083
+ });
1917
2084
  this.disposeActiveSelector();
1918
2085
  this.editorContainer.clear();
1919
2086
  this.editorContainer.addChild(this.extensionSelector);
@@ -1932,6 +2099,40 @@ export class InteractiveMode {
1932
2099
  this.ui.setFocus(this.editor);
1933
2100
  this.ui.requestRender();
1934
2101
  }
2102
+ async showConfigurationIndex() {
2103
+ const choice = await this.showExtensionSelector("Configuration", [
2104
+ "Settings",
2105
+ "Model",
2106
+ "Model cycling",
2107
+ "Provider login",
2108
+ "Provider logout",
2109
+ "Project trust",
2110
+ "Resources, extensions, and MCP adapters",
2111
+ ], undefined, true);
2112
+ switch (choice) {
2113
+ case "Settings":
2114
+ await this.showSettingsSelector();
2115
+ break;
2116
+ case "Model":
2117
+ this.showModelSelector();
2118
+ break;
2119
+ case "Model cycling":
2120
+ await this.showModelsSelector();
2121
+ break;
2122
+ case "Provider login":
2123
+ await this.handleLoginCommand();
2124
+ break;
2125
+ case "Provider logout":
2126
+ this.showOAuthSelector("logout");
2127
+ break;
2128
+ case "Project trust":
2129
+ this.showTrustSelector();
2130
+ break;
2131
+ case "Resources, extensions, and MCP adapters":
2132
+ this.showStatus("Run apex-code config to manage resources, extensions, and MCP adapters.");
2133
+ break;
2134
+ }
2135
+ }
1935
2136
  /**
1936
2137
  * Show a confirmation dialog for extensions.
1937
2138
  */
@@ -2243,6 +2444,8 @@ export class InteractiveMode {
2243
2444
  const wasBashMode = this.isBashMode;
2244
2445
  this.isBashMode = text.trimStart().startsWith("!");
2245
2446
  if (wasBashMode !== this.isBashMode) {
2447
+ if (this.isBashMode)
2448
+ this.offerFirstUseHint("bash");
2246
2449
  this.updateEditorBorderColor();
2247
2450
  }
2248
2451
  };
@@ -2274,7 +2477,7 @@ export class InteractiveMode {
2274
2477
  if (image) {
2275
2478
  const tmpDir = os.tmpdir();
2276
2479
  const ext = extensionForImageMimeType(image.mimeType) ?? "png";
2277
- const fileName = `pi-clipboard-${crypto.randomUUID()}.${ext}`;
2480
+ const fileName = `apex-code-clipboard-${crypto.randomUUID()}.${ext}`;
2278
2481
  const filePath = path.join(tmpDir, fileName);
2279
2482
  fs.writeFileSync(filePath, Buffer.from(image.bytes));
2280
2483
  this.editor.insertTextAtCursor?.(filePath);
@@ -2291,15 +2494,31 @@ export class InteractiveMode {
2291
2494
  // Silently ignore clipboard errors (may not have permission, etc.)
2292
2495
  }
2293
2496
  }
2497
+ handleStartupSubmit(text) {
2498
+ this.editor.setText(text);
2499
+ this.showStatus("Startup is still in progress");
2500
+ }
2294
2501
  setupEditorSubmitHandler() {
2295
2502
  this.defaultEditor.onSubmit = async (text) => {
2296
2503
  text = text.trim();
2297
2504
  if (!text)
2298
2505
  return;
2506
+ // A mode chosen in /settings is written asynchronously. Awaiting it here
2507
+ // keeps the store authoritative before any tool call can read it, so a
2508
+ // switch *out* of bypassPermissions can never be raced by the next turn.
2509
+ if (this.pendingPermissionModeWrite) {
2510
+ await this.pendingPermissionModeWrite;
2511
+ this.pendingPermissionModeWrite = undefined;
2512
+ }
2299
2513
  // Handle commands
2514
+ if (text === "/config") {
2515
+ this.editor.setText("");
2516
+ await this.showConfigurationIndex();
2517
+ return;
2518
+ }
2300
2519
  if (text === "/settings") {
2301
- this.showSettingsSelector();
2302
2520
  this.editor.setText("");
2521
+ await this.showSettingsSelector();
2303
2522
  return;
2304
2523
  }
2305
2524
  if (text === "/scoped-models") {
@@ -2313,6 +2532,12 @@ export class InteractiveMode {
2313
2532
  await this.handleModelCommand(searchTerm);
2314
2533
  return;
2315
2534
  }
2535
+ if (text === "/thinking" || text.startsWith("/thinking ")) {
2536
+ const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
2537
+ this.editor.setText("");
2538
+ this.handleThinkingCommand(searchTerm);
2539
+ return;
2540
+ }
2316
2541
  if (text === "/export" || text.startsWith("/export ")) {
2317
2542
  await this.handleExportCommand(text);
2318
2543
  this.editor.setText("");
@@ -2339,7 +2564,7 @@ export class InteractiveMode {
2339
2564
  return;
2340
2565
  }
2341
2566
  if (text === "/session") {
2342
- this.handleSessionCommand();
2567
+ await this.handleSessionCommand();
2343
2568
  this.editor.setText("");
2344
2569
  return;
2345
2570
  }
@@ -2348,6 +2573,11 @@ export class InteractiveMode {
2348
2573
  this.editor.setText("");
2349
2574
  return;
2350
2575
  }
2576
+ if (text === "/resources") {
2577
+ this.showLoadedResources({ force: true });
2578
+ this.editor.setText("");
2579
+ return;
2580
+ }
2351
2581
  if (text === "/hotkeys") {
2352
2582
  this.handleHotkeysCommand();
2353
2583
  this.editor.setText("");
@@ -2488,7 +2718,7 @@ export class InteractiveMode {
2488
2718
  this.footer.invalidate();
2489
2719
  switch (event.type) {
2490
2720
  case "agent_start":
2491
- this.pendingTools.clear();
2721
+ this.clearPendingTools();
2492
2722
  if (this.settingsManager.getShowTerminalProgress()) {
2493
2723
  this.ui.terminal.setProgress(true);
2494
2724
  }
@@ -2540,6 +2770,7 @@ export class InteractiveMode {
2540
2770
  this.streamingMessage = event.message;
2541
2771
  this.chatContainer.addChild(this.streamingComponent);
2542
2772
  this.streamingComponent.updateContent(this.streamingMessage, true);
2773
+ this.updateEditorBorderColor();
2543
2774
  this.ui.requestRender();
2544
2775
  }
2545
2776
  break;
@@ -2557,6 +2788,7 @@ export class InteractiveMode {
2557
2788
  component.setExpanded(this.toolOutputExpanded);
2558
2789
  this.chatContainer.addChild(component);
2559
2790
  this.pendingTools.set(content.id, component);
2791
+ this.offerFirstUseHint("tool-expand");
2560
2792
  }
2561
2793
  else {
2562
2794
  const component = this.pendingTools.get(content.id);
@@ -2594,7 +2826,7 @@ export class InteractiveMode {
2594
2826
  isError: true,
2595
2827
  });
2596
2828
  }
2597
- this.pendingTools.clear();
2829
+ this.clearPendingTools();
2598
2830
  }
2599
2831
  else {
2600
2832
  // Args are now complete - trigger diff computation for edit tools
@@ -2606,6 +2838,7 @@ export class InteractiveMode {
2606
2838
  this.streamingComponent = undefined;
2607
2839
  this.streamingMessage = undefined;
2608
2840
  this.footer.invalidate();
2841
+ this.updateEditorBorderColor();
2609
2842
  }
2610
2843
  this.ui.requestRender();
2611
2844
  break;
@@ -2654,7 +2887,7 @@ export class InteractiveMode {
2654
2887
  this.streamingComponent = undefined;
2655
2888
  this.streamingMessage = undefined;
2656
2889
  }
2657
- this.pendingTools.clear();
2890
+ this.clearPendingTools();
2658
2891
  this.ui.requestRender();
2659
2892
  break;
2660
2893
  case "agent_settled":
@@ -2691,9 +2924,21 @@ export class InteractiveMode {
2691
2924
  }
2692
2925
  }
2693
2926
  else if (event.result) {
2927
+ const entries = this.sessionManager.buildContextEntries();
2928
+ if (entries[0]?.type !== "compaction") {
2929
+ throw new Error("Completed compaction is missing from the session context");
2930
+ }
2694
2931
  this.chatContainer.clear();
2695
- this.rebuildChatFromMessages();
2932
+ // The latest compaction is prepended for model context; append it below at its chronological position.
2933
+ this.renderSessionEntries(entries.slice(1));
2696
2934
  this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
2935
+ if (event.result.usage) {
2936
+ this.addCompactionCostNotice({
2937
+ type: "compaction_cost",
2938
+ kind: "compaction",
2939
+ usage: event.result.usage,
2940
+ });
2941
+ }
2697
2942
  this.footer.invalidate();
2698
2943
  }
2699
2944
  else if (event.errorMessage) {
@@ -2766,6 +3011,19 @@ export class InteractiveMode {
2766
3011
  : message.content.filter((c) => c.type === "text");
2767
3012
  return textBlocks.map((c) => c.text).join("");
2768
3013
  }
3014
+ /** Show a managed-tool status update in the chat. */
3015
+ showManagedToolStatus(status) {
3016
+ if (!this.managedToolStatusStarted) {
3017
+ this.chatContainer.addChild(new Spacer(1));
3018
+ this.managedToolStatusStarted = true;
3019
+ }
3020
+ const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
3021
+ const color = status.type === "warning" ? "warning" : "dim";
3022
+ this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
3023
+ this.lastStatusSpacer = undefined;
3024
+ this.lastStatusText = undefined;
3025
+ this.ui.requestRender();
3026
+ }
2769
3027
  /**
2770
3028
  * Show a status message in the chat.
2771
3029
  *
@@ -2886,7 +3144,7 @@ export class InteractiveMode {
2886
3144
  }
2887
3145
  }
2888
3146
  renderSessionItems(items, options = {}) {
2889
- this.pendingTools.clear();
3147
+ this.clearPendingTools();
2890
3148
  const renderedPendingTools = new Map();
2891
3149
  // Cache-miss notices are not persisted; re-derive them from the full entry
2892
3150
  // list and re-inject them after the assistant messages that paid for them.
@@ -2902,6 +3160,10 @@ export class InteractiveMode {
2902
3160
  this.addCustomEntryToChat(item);
2903
3161
  continue;
2904
3162
  }
3163
+ if (isCompactionCostNotice(item)) {
3164
+ this.addCompactionCostNotice(item);
3165
+ continue;
3166
+ }
2905
3167
  const message = item;
2906
3168
  // Assistant messages need special handling for tool calls
2907
3169
  if (message.role === "assistant") {
@@ -2969,10 +3231,28 @@ export class InteractiveMode {
2969
3231
  if (entry.type === "custom") {
2970
3232
  return [entry];
2971
3233
  }
2972
- return sessionEntryToContextMessages(entry);
3234
+ const messages = sessionEntryToContextMessages(entry);
3235
+ if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
3236
+ return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
3237
+ }
3238
+ return messages;
2973
3239
  });
2974
3240
  this.renderSessionItems(items, options);
2975
3241
  }
3242
+ /**
3243
+ * Render billing usage for a compaction or branch summary. The notice is derived
3244
+ * from persisted summary usage and is not stored as a separate session entry.
3245
+ */
3246
+ addCompactionCostNotice(notice) {
3247
+ if (!this.settingsManager.getShowCacheMissNotices())
3248
+ return;
3249
+ const { usage } = notice;
3250
+ const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
3251
+ const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
3252
+ const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
3253
+ this.chatContainer.addChild(new Spacer(1));
3254
+ this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
3255
+ }
2976
3256
  /**
2977
3257
  * Show a transcript notice when a completed assistant message paid for a
2978
3258
  * significant cache miss. Only states observable facts: the miss itself,
@@ -3024,7 +3304,7 @@ export class InteractiveMode {
3024
3304
  if (this.chatContainer.children.length > 0) {
3025
3305
  this.chatContainer.addChild(new Spacer(1));
3026
3306
  }
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));
3307
+ 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
3308
  }
3029
3309
  async getUserInput() {
3030
3310
  const queuedInput = this.pendingUserInputs.shift();
@@ -3137,7 +3417,7 @@ export class InteractiveMode {
3137
3417
  this.ui.stop();
3138
3418
  }
3139
3419
  catch { }
3140
- console.error("pi exiting due to uncaughtException:");
3420
+ console.error("apex-code exiting due to uncaughtException:");
3141
3421
  console.error(error);
3142
3422
  process.exit(1);
3143
3423
  }
@@ -3183,6 +3463,34 @@ export class InteractiveMode {
3183
3463
  const uncaughtExceptionHandler = (error) => this.uncaughtCrash(error);
3184
3464
  process.prependListener("uncaughtException", uncaughtExceptionHandler);
3185
3465
  this.signalCleanupHandlers.push(() => process.off("uncaughtException", uncaughtExceptionHandler));
3466
+ // Lend the terminal back to the supervisor when it needs to draw a sandbox
3467
+ // escalation prompt. The supervisor owns that decision (ADR 0023) and this
3468
+ // process cannot read the answer anyway -- bwrap --new-session leaves it with no
3469
+ // controlling terminal. `ui.stop()` is the same restore Ctrl+Z already performs,
3470
+ // so the terminal is in cooked mode with a visible cursor while the human answers.
3471
+ const handoffDirectory = process.env[TERMINAL_HANDOFF_PATH_VARIABLE];
3472
+ if (handoffDirectory) {
3473
+ const observer = observeTerminalHandoff(handoffDirectory, {
3474
+ suspend: () => {
3475
+ try {
3476
+ this.ui.stop();
3477
+ }
3478
+ catch {
3479
+ // A terminal we cannot restore still leaves the prompt readable.
3480
+ }
3481
+ },
3482
+ resume: () => {
3483
+ try {
3484
+ this.ui.start();
3485
+ this.ui.requestRender(true);
3486
+ }
3487
+ catch {
3488
+ // Nothing further to try; the next render request recovers.
3489
+ }
3490
+ },
3491
+ });
3492
+ this.signalCleanupHandlers.push(() => observer.stop());
3493
+ }
3186
3494
  }
3187
3495
  unregisterSignalHandlers() {
3188
3496
  for (const cleanup of this.signalCleanupHandlers) {
@@ -3244,6 +3552,7 @@ export class InteractiveMode {
3244
3552
  this.editor.addToHistory?.(text);
3245
3553
  this.editor.setText("");
3246
3554
  await this.session.prompt(text, { streamingBehavior: "followUp" });
3555
+ this.offerFirstUseHint("queue");
3247
3556
  this.updatePendingMessagesDisplay();
3248
3557
  this.ui.requestRender();
3249
3558
  }
@@ -3263,6 +3572,7 @@ export class InteractiveMode {
3263
3572
  }
3264
3573
  }
3265
3574
  updateEditorBorderColor() {
3575
+ this.defaultEditor.setModeLabel(this.isBashMode ? "bash" : this.session.isStreaming ? "busy" : undefined);
3266
3576
  if (this.isBashMode) {
3267
3577
  this.editor.borderColor = theme.getBashModeBorderColor();
3268
3578
  }
@@ -3323,6 +3633,7 @@ export class InteractiveMode {
3323
3633
  this.showStatus(`Tool output: ${expanded ? "expanded" : "collapsed"}`);
3324
3634
  }
3325
3635
  toggleThinkingBlockVisibility() {
3636
+ this.offerFirstUseHint("thinking");
3326
3637
  this.hideThinkingBlock = !this.hideThinkingBlock;
3327
3638
  this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
3328
3639
  // Rebuild chat from session messages
@@ -3374,7 +3685,7 @@ export class InteractiveMode {
3374
3685
  showNewVersionNotification(release) {
3375
3686
  const action = theme.fg("accent", `${APP_NAME} update`);
3376
3687
  const updateInstruction = theme.fg("muted", `New version ${release.version} is available. Run `) + action;
3377
- const changelogUrl = "https://pi.dev/changelog";
3688
+ const changelogUrl = "https://github.com/Fchery87/apex-code/blob/main/packages/coding-agent/CHANGELOG.md";
3378
3689
  const changelogLink = getCapabilities().hyperlinks
3379
3690
  ? hyperlink(theme.fg("accent", changelogUrl), changelogUrl)
3380
3691
  : theme.fg("accent", changelogUrl);
@@ -3606,11 +3917,63 @@ export class InteractiveMode {
3606
3917
  this.ui.setFocus(created.focus);
3607
3918
  this.ui.requestRender();
3608
3919
  }
3609
- showSettingsSelector() {
3920
+ /**
3921
+ * Resyncs the footer from the store. Called on every session swap, not just at
3922
+ * startup: `applyRuntimeSettings` also repoints the cwd, and cwd selects which
3923
+ * project-scope permission file applies, so the mode genuinely can differ
3924
+ * between two sessions in one process. A footer left showing the old value
3925
+ * would under-report bypassPermissions, which is the one direction that matters.
3926
+ */
3927
+ async refreshFooterPermissionMode() {
3928
+ const resolution = await this.session.getPermissionMode();
3929
+ this.footer.setPermissionMode(resolution?.mode ?? "default");
3930
+ if (this.isInitialized) {
3931
+ this.ui.requestRender();
3932
+ }
3933
+ }
3934
+ /**
3935
+ * Writes the mode to user scope, then reports what is actually in force. The
3936
+ * write can be outranked by `--permission-mode` or a project file, and a
3937
+ * settings row that silently does nothing is worse than no row at all.
3938
+ */
3939
+ async applyPermissionMode(mode) {
3940
+ let resolution;
3941
+ try {
3942
+ const write = this.session.setPermissionMode(mode);
3943
+ this.pendingPermissionModeWrite = write.then(() => { }, () => { });
3944
+ resolution = await write;
3945
+ }
3946
+ catch (error) {
3947
+ // Never swallow this. A permission mode the user believes they set, and
3948
+ // did not, is the one outcome worse than the prompt they were escaping.
3949
+ const detail = error instanceof Error ? error.message : String(error);
3950
+ this.chatContainer.addChild(new Text(theme.fg("error", `Permission mode could not be saved: ${detail}`), 1, 0));
3951
+ this.ui.requestRender();
3952
+ return;
3953
+ }
3954
+ if (!resolution)
3955
+ return;
3956
+ const outranked = resolution.mode !== mode;
3957
+ const source = PERMISSION_MODE_OVERRIDE_HINTS[resolution.origin] ?? resolution.origin;
3958
+ const line = outranked
3959
+ ? `Permission mode saved as ${mode}, but ${source} sets ${resolution.mode} for this session.`
3960
+ : `Permission mode set to ${mode}.`;
3961
+ this.chatContainer.addChild(new Text(theme.fg(outranked ? "warning" : "muted", line), 1, 0));
3962
+ this.footer.setPermissionMode(resolution.mode);
3963
+ this.ui.requestRender();
3964
+ }
3965
+ async showSettingsSelector() {
3966
+ const permissionMode = await this.session.getPermissionMode();
3610
3967
  this.showSelector((done) => {
3611
3968
  let selector;
3969
+ const defaultProvider = this.settingsManager.getDefaultProvider();
3970
+ const defaultModelId = this.settingsManager.getDefaultModel();
3971
+ const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
3612
3972
  selector = new SettingsSelectorComponent({
3613
3973
  autoCompact: this.session.autoCompactionEnabled,
3974
+ defaultModel,
3975
+ currentModel: this.session.model,
3976
+ availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
3614
3977
  showImages: this.settingsManager.getShowImages(),
3615
3978
  imageWidthCells: this.settingsManager.getImageWidthCells(),
3616
3979
  autoResizeImages: this.settingsManager.getImageAutoResize(),
@@ -3620,20 +3983,22 @@ export class InteractiveMode {
3620
3983
  followUpMode: this.session.followUpMode,
3621
3984
  transport: this.settingsManager.getTransport(),
3622
3985
  httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
3623
- thinkingLevel: this.session.thinkingLevel,
3624
- availableThinkingLevels: this.session.getAvailableThinkingLevels(),
3625
- currentTheme: this.settingsManager.getThemeSetting() || "dark",
3986
+ thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
3987
+ availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
3988
+ modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
3989
+ currentTheme: this.themeController.getThemeSelection() || "dark",
3626
3990
  terminalTheme: this.themeController.getTerminalTheme(),
3627
3991
  availableThemes: getAvailableThemes(),
3628
3992
  hideThinkingBlock: this.hideThinkingBlock,
3629
3993
  mermaidRenderingMode: this.settingsManager.getMermaidRenderingMode(),
3630
3994
  collapseChangelog: this.settingsManager.getCollapseChangelog(),
3631
- enableInstallTelemetry: this.settingsManager.getEnableInstallTelemetry(),
3995
+ sendProviderAttribution: this.settingsManager.getSendProviderAttribution(),
3632
3996
  doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
3633
3997
  treeFilterMode: this.settingsManager.getTreeFilterMode(),
3634
3998
  showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
3635
3999
  showCacheMissNotices: this.settingsManager.getShowCacheMissNotices(),
3636
4000
  defaultProjectTrust: this.settingsManager.getDefaultProjectTrust(),
4001
+ permissionMode,
3637
4002
  editorPaddingX: this.settingsManager.getEditorPaddingX(),
3638
4003
  outputPad: this.settingsManager.getOutputPad(),
3639
4004
  autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
@@ -3641,6 +4006,7 @@ export class InteractiveMode {
3641
4006
  clearOnShrink: this.settingsManager.getClearOnShrink(),
3642
4007
  showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
3643
4008
  tuiMode: this.ui.mode,
4009
+ fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
3644
4010
  fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
3645
4011
  warnings: this.settingsManager.getWarnings(),
3646
4012
  }, {
@@ -3689,14 +4055,30 @@ export class InteractiveMode {
3689
4055
  configureHttpDispatcher(timeoutMs);
3690
4056
  this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
3691
4057
  },
3692
- onThinkingLevelChange: (level) => {
3693
- this.session.setThinkingLevel(level);
3694
- this.footer.invalidate();
3695
- this.updateEditorBorderColor();
4058
+ onModelThinkingLevelChange: (provider, modelId, level) => {
4059
+ this.settingsManager.setModelThinkingLevel(provider, modelId, level);
4060
+ // If the override is for the current model, apply it to the session too
4061
+ const current = this.session.model;
4062
+ if (current && current.provider === provider && current.id === modelId) {
4063
+ this.session.setThinkingLevel(level);
4064
+ this.footer.invalidate();
4065
+ this.updateEditorBorderColor();
4066
+ }
4067
+ },
4068
+ onModelThinkingLevelRemove: (provider, modelId) => {
4069
+ this.settingsManager.removeModelThinkingLevel(provider, modelId);
4070
+ // If the override was for the current model, revert to global default
4071
+ const current = this.session.model;
4072
+ if (current && current.provider === provider && current.id === modelId) {
4073
+ const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
4074
+ this.session.setThinkingLevel(globalDefault);
4075
+ this.footer.invalidate();
4076
+ this.updateEditorBorderColor();
4077
+ }
3696
4078
  },
3697
4079
  onThemeChange: (themeSetting) => {
3698
4080
  this.settingsManager.setTheme(themeSetting);
3699
- void this.themeController.applyFromSettings();
4081
+ void this.themeController.setThemeSetting(themeSetting);
3700
4082
  },
3701
4083
  onThemePreview: (themeName) => this.themeController.preview(themeName),
3702
4084
  onHideThinkingBlockChange: (hidden) => {
@@ -3722,8 +4104,8 @@ export class InteractiveMode {
3722
4104
  onCollapseChangelogChange: (collapsed) => {
3723
4105
  this.settingsManager.setCollapseChangelog(collapsed);
3724
4106
  },
3725
- onEnableInstallTelemetryChange: (enabled) => {
3726
- this.settingsManager.setEnableInstallTelemetry(enabled);
4107
+ onSendProviderAttributionChange: (enabled) => {
4108
+ this.settingsManager.setSendProviderAttribution(enabled);
3727
4109
  },
3728
4110
  onQuietStartupChange: (enabled) => {
3729
4111
  this.settingsManager.setQuietStartup(enabled);
@@ -3731,6 +4113,9 @@ export class InteractiveMode {
3731
4113
  onDefaultProjectTrustChange: (defaultProjectTrust) => {
3732
4114
  this.settingsManager.setDefaultProjectTrust(defaultProjectTrust);
3733
4115
  },
4116
+ onPermissionModeChange: (mode) => {
4117
+ void this.applyPermissionMode(mode);
4118
+ },
3734
4119
  onDoubleEscapeActionChange: (action) => {
3735
4120
  this.settingsManager.setDoubleEscapeAction(action);
3736
4121
  },
@@ -3795,6 +4180,9 @@ export class InteractiveMode {
3795
4180
  this.statusContainer.clear();
3796
4181
  this.showStatus(`TUI mode: ${mode}`);
3797
4182
  },
4183
+ onFullscreenExitOutputChange: (output) => {
4184
+ this.settingsManager.setFullscreenExitOutput(output);
4185
+ },
3798
4186
  onFullscreenScrollbarChange: (mode) => {
3799
4187
  this.settingsManager.setFullscreenScrollbar(mode);
3800
4188
  this.applyFullscreenScrollbarSetting();
@@ -3810,6 +4198,44 @@ export class InteractiveMode {
3810
4198
  return { component: selector, focus: selector.getSettingsList() };
3811
4199
  });
3812
4200
  }
4201
+ handleThinkingCommand(searchTerm) {
4202
+ const availableLevels = this.session.getAvailableThinkingLevels();
4203
+ if (!searchTerm) {
4204
+ this.showThinkingSelector();
4205
+ return;
4206
+ }
4207
+ const normalized = searchTerm.trim().toLowerCase();
4208
+ const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
4209
+ if (!level) {
4210
+ this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
4211
+ return;
4212
+ }
4213
+ this.selectThinkingLevel(level, false);
4214
+ }
4215
+ selectThinkingLevel(level, persist) {
4216
+ try {
4217
+ this.session.setThinkingLevel(level, { persist });
4218
+ this.footer.invalidate();
4219
+ this.updateEditorBorderColor();
4220
+ this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
4221
+ }
4222
+ catch (error) {
4223
+ this.showError(error instanceof Error ? error.message : String(error));
4224
+ }
4225
+ }
4226
+ showThinkingSelector() {
4227
+ this.showSelector((done) => {
4228
+ const selectLevel = (level, persist) => {
4229
+ this.selectThinkingLevel(level, persist);
4230
+ done();
4231
+ };
4232
+ const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
4233
+ done();
4234
+ this.ui.requestRender();
4235
+ }, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
4236
+ return { component: selector, focus: selector };
4237
+ });
4238
+ }
3813
4239
  async handleModelCommand(searchTerm) {
3814
4240
  if (!searchTerm) {
3815
4241
  this.showModelSelector();
@@ -3818,7 +4244,7 @@ export class InteractiveMode {
3818
4244
  const model = await this.findExactModelMatch(searchTerm);
3819
4245
  if (model) {
3820
4246
  try {
3821
- await this.session.setModel(model);
4247
+ await this.session.setModel(model, { persist: false });
3822
4248
  this.footer.invalidate();
3823
4249
  this.updateEditorBorderColor();
3824
4250
  this.showStatus(`Model: ${model.id}`);
@@ -3847,7 +4273,7 @@ export class InteractiveMode {
3847
4273
  controller.abort();
3848
4274
  }, 15_000);
3849
4275
  try {
3850
- const result = await this.session.modelRuntime.refresh({ signal: controller.signal });
4276
+ const result = await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
3851
4277
  if (result.aborted && timedOut) {
3852
4278
  this.showWarning("Model refresh timed out; searching cached models.");
3853
4279
  }
@@ -3935,7 +4361,7 @@ export class InteractiveMode {
3935
4361
  onSelect: (selection) => {
3936
4362
  trustStore.setMany(selection.updates);
3937
4363
  done();
3938
- this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart pi for this to take effect.`);
4364
+ this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart ${APP_NAME} for this to take effect.`);
3939
4365
  },
3940
4366
  onCancel: () => {
3941
4367
  done();
@@ -3947,13 +4373,13 @@ export class InteractiveMode {
3947
4373
  }
3948
4374
  showModelSelector(initialSearchInput) {
3949
4375
  this.showSelector((done) => {
3950
- const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRuntime, this.session.scopedModels, async (model) => {
4376
+ const selectModel = async (model, persist) => {
3951
4377
  try {
3952
- await this.session.setModel(model);
4378
+ await this.session.setModel(model, { persist });
3953
4379
  this.footer.invalidate();
3954
4380
  this.updateEditorBorderColor();
3955
4381
  done();
3956
- this.showStatus(`Model: ${model.id}`);
4382
+ this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
3957
4383
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
3958
4384
  this.checkDaxnutsEasterEgg(model);
3959
4385
  }
@@ -3961,10 +4387,13 @@ export class InteractiveMode {
3961
4387
  done();
3962
4388
  this.showError(error instanceof Error ? error.message : String(error));
3963
4389
  }
3964
- }, () => {
4390
+ };
4391
+ const defaultProvider = this.settingsManager.getDefaultProvider();
4392
+ const defaultModel = this.settingsManager.getDefaultModel();
4393
+ const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
3965
4394
  done();
3966
4395
  this.ui.requestRender();
3967
- }, initialSearchInput);
4396
+ }, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
3968
4397
  return { component: selector, focus: selector, dispose: () => selector.dispose() };
3969
4398
  });
3970
4399
  }
@@ -4035,8 +4464,7 @@ export class InteractiveMode {
4035
4464
  this.ui.requestRender();
4036
4465
  },
4037
4466
  });
4038
- void this.session.modelRuntime
4039
- .refresh({ signal: controller.signal })
4467
+ void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
4040
4468
  .then((result) => {
4041
4469
  if (disposed)
4042
4470
  return;
@@ -4521,7 +4949,11 @@ export class InteractiveMode {
4521
4949
  if (isUnknownModel(previousModel)) {
4522
4950
  const availableModels = this.session.modelRuntime.getAvailableSnapshot();
4523
4951
  const providerModels = availableModels.filter((model) => model.provider === providerId);
4524
- if (!hasDefaultModelProvider(providerId)) {
4952
+ // Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
4953
+ if (providerId === "llama.cpp") {
4954
+ selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
4955
+ }
4956
+ else if (!hasDefaultModelProvider(providerId)) {
4525
4957
  selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
4526
4958
  }
4527
4959
  else if (providerModels.length === 0) {
@@ -4535,7 +4967,7 @@ export class InteractiveMode {
4535
4967
  }
4536
4968
  else {
4537
4969
  try {
4538
- await this.session.setModel(selectedModel);
4970
+ await this.session.setModel(selectedModel, { persist: true });
4539
4971
  }
4540
4972
  catch (error) {
4541
4973
  selectedModel = undefined;
@@ -4590,7 +5022,7 @@ export class InteractiveMode {
4590
5022
  this.ui.requestRender();
4591
5023
  };
4592
5024
  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);
5025
+ dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside Apex Code.`, [], true);
4594
5026
  this.editorContainer.clear();
4595
5027
  this.editorContainer.addChild(dialog);
4596
5028
  this.ui.setFocus(dialog);
@@ -4827,7 +5259,9 @@ export class InteractiveMode {
4827
5259
  this.showStatus(`Session exported to: ${filePath}`);
4828
5260
  }
4829
5261
  else {
4830
- const filePath = await this.session.exportToHtml(outputPath);
5262
+ const filePath = await this.session.exportToHtml(outputPath, {
5263
+ themeName: theme.name,
5264
+ });
4831
5265
  this.showStatus(`Session exported to: ${filePath}`);
4832
5266
  }
4833
5267
  }
@@ -4903,89 +5337,74 @@ export class InteractiveMode {
4903
5337
  }
4904
5338
  }
4905
5339
  async handleShareCommand() {
4906
- // Check if gh is available and logged in
5340
+ // Check before confirmation so a user without a working GitHub CLI gets an actionable error.
4907
5341
  try {
4908
5342
  const authResult = spawnSync("gh", ["auth", "status"], { encoding: "utf-8" });
4909
5343
  if (authResult.status !== 0) {
4910
- this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");
5344
+ this.showError(formatShareUnavailableMessage("unauthenticated"));
4911
5345
  return;
4912
5346
  }
4913
5347
  }
4914
5348
  catch {
4915
- this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");
4916
- return;
4917
- }
4918
- // Export to a temp file
4919
- const tmpFile = path.join(os.tmpdir(), "session.html");
4920
- 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"}`);
5349
+ this.showError(formatShareUnavailableMessage("missing"));
4925
5350
  return;
4926
5351
  }
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
- };
5352
+ let loader;
5353
+ let proc;
4952
5354
  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 }));
5355
+ const result = await publishSessionShare({
5356
+ 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."),
5357
+ exportToHtml: async (exportPath) => {
5358
+ await this.session.exportToHtml(exportPath);
5359
+ },
5360
+ publishGist: (exportPath) => new Promise((resolve, reject) => {
5361
+ loader = new BorderedLoader(this.ui, theme, "Creating secret GitHub Gist...");
5362
+ this.editorContainer.clear();
5363
+ this.editorContainer.addChild(loader);
5364
+ this.ui.setFocus(loader);
5365
+ this.ui.requestRender();
5366
+ loader.onAbort = () => proc?.kill();
5367
+ proc = spawn("gh", ["gist", "create", "--public=false", exportPath]);
5368
+ let stdout = "";
5369
+ let stderr = "";
5370
+ proc.stdout?.on("data", (data) => {
5371
+ stdout += data.toString();
5372
+ });
5373
+ proc.stderr?.on("data", (data) => {
5374
+ stderr += data.toString();
5375
+ });
5376
+ proc.on("error", reject);
5377
+ proc.on("close", (code) => {
5378
+ if (loader?.signal.aborted)
5379
+ reject(new Error("Share cancelled"));
5380
+ else if (code !== 0)
5381
+ reject(new Error(stderr.trim() || "Unknown GitHub CLI error"));
5382
+ else
5383
+ resolve(stdout);
5384
+ });
5385
+ }),
4964
5386
  });
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");
5387
+ if (result.kind === "cancelled") {
5388
+ this.showStatus("Share cancelled");
4979
5389
  return;
4980
5390
  }
4981
- // Create the preview URL
4982
- const previewUrl = getShareViewerUrl(gistId);
4983
- this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
5391
+ const previewUrl = getShareViewerUrl(result.gistId);
5392
+ this.showStatus(previewUrl
5393
+ ? `Secret GitHub Gist: ${result.gistUrl}\nConfigured preview: ${previewUrl}`
5394
+ : `Secret GitHub Gist: ${result.gistUrl}`);
4984
5395
  }
4985
5396
  catch (error) {
4986
- if (!loader.signal.aborted) {
4987
- restoreEditor();
4988
- this.showError(`Failed to create gist: ${error instanceof Error ? error.message : "Unknown error"}`);
5397
+ if (error instanceof Error && error.message === "Share cancelled")
5398
+ this.showStatus("Share cancelled");
5399
+ else
5400
+ this.showError(`Failed to create secret GitHub Gist: ${error instanceof Error ? error.message : "Unknown error"}`);
5401
+ }
5402
+ finally {
5403
+ if (loader) {
5404
+ loader.dispose();
5405
+ this.editorContainer.clear();
5406
+ this.editorContainer.addChild(this.editor);
5407
+ this.ui.setFocus(this.editor);
4989
5408
  }
4990
5409
  }
4991
5410
  }
@@ -5031,7 +5450,7 @@ export class InteractiveMode {
5031
5450
  this.chatContainer.addChild(new Text(theme.fg("dim", `Session name set: ${sessionName ?? name}`), 1, 0));
5032
5451
  this.ui.requestRender();
5033
5452
  }
5034
- handleSessionCommand() {
5453
+ async handleSessionCommand() {
5035
5454
  const stats = this.session.getSessionStats();
5036
5455
  const sessionName = this.sessionManager.getSessionName();
5037
5456
  const entries = this.sessionManager.getEntries();
@@ -5084,6 +5503,26 @@ export class InteractiveMode {
5084
5503
  : `\n${theme.fg("dim", "Cache Re-billed:")} ${detail}`;
5085
5504
  }
5086
5505
  }
5506
+ // Role and latency: the two dimensions the durable ledger (roadmap Phase 8)
5507
+ // carries that session entries never did. Scoped to this session's rows only
5508
+ // -- a ledger that also served other sessions must never leak their cost in.
5509
+ const usagePerformanceSamples = (await this.session.modelRuntime.listUsagePerformanceSamples()).filter((sample) => sample.sessionId === stats.sessionId);
5510
+ if (usagePerformanceSamples.length > 0) {
5511
+ const avgTtftMs = usagePerformanceSamples.reduce((sum, sample) => sum + sample.ttftMs, 0) / usagePerformanceSamples.length;
5512
+ const avgGenerationMs = usagePerformanceSamples.reduce((sum, sample) => sum + sample.generationMs, 0) /
5513
+ usagePerformanceSamples.length;
5514
+ info += `\n\n${theme.bold("Latency")}\n`;
5515
+ info += `${theme.fg("dim", "Avg TTFT:")} ${Math.round(avgTtftMs)}ms\n`;
5516
+ info += `${theme.fg("dim", "Avg Generation:")} ${Math.round(avgGenerationMs)}ms`;
5517
+ const roleRows = aggregateUsagePerformance(usagePerformanceSamples, "role");
5518
+ if (roleRows.length > 0) {
5519
+ info += `\n\n${theme.bold("Roles")}`;
5520
+ for (const row of roleRows) {
5521
+ const requestLabel = row.sampleCount === 1 ? "1 request" : `${row.sampleCount} requests`;
5522
+ info += `\n ${theme.fg("dim", `${row.key}:`)} $${row.cost.toFixed(3)} ${theme.fg("dim", `(${requestLabel})`)}`;
5523
+ }
5524
+ }
5525
+ }
5087
5526
  this.chatContainer.addChild(new Spacer(1));
5088
5527
  this.chatContainer.addChild(new Text(info, 1, 0));
5089
5528
  this.ui.requestRender();
@@ -5364,7 +5803,7 @@ export class InteractiveMode {
5364
5803
  // Ignore, will be emitted as an event
5365
5804
  }
5366
5805
  }
5367
- stop() {
5806
+ stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
5368
5807
  this.disposeActiveSelector();
5369
5808
  if (this.settingsManager.getShowTerminalProgress()) {
5370
5809
  this.ui.terminal.setProgress(false);
@@ -5378,7 +5817,7 @@ export class InteractiveMode {
5378
5817
  this.unsubscribe();
5379
5818
  }
5380
5819
  if (this.isInitialized) {
5381
- this.stopInteractiveTui();
5820
+ this.stopInteractiveTui(fullscreenExitOutput);
5382
5821
  this.isInitialized = false;
5383
5822
  }
5384
5823
  this.unregisterSignalHandlers();