@gajae-code/coding-agent 0.17.1 → 0.17.4

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 (375) hide show
  1. package/CHANGELOG.md +266 -0
  2. package/dist/types/async/job-manager.d.ts +7 -6
  3. package/dist/types/cli/daemon-cli.d.ts +3 -1
  4. package/dist/types/cli/github-release.d.ts +17 -0
  5. package/dist/types/cli/public-command-entry.d.ts +56 -0
  6. package/dist/types/cli/public-command-errors.d.ts +126 -0
  7. package/dist/types/cli/public-command-evidence.d.ts +99 -0
  8. package/dist/types/cli/public-command-help.d.ts +65 -0
  9. package/dist/types/cli/public-command-registry.d.ts +69 -0
  10. package/dist/types/cli/session-picker.d.ts +1 -1
  11. package/dist/types/cli/setup-cli.d.ts +1 -0
  12. package/dist/types/cli/skills-cli.d.ts +5 -0
  13. package/dist/types/cli.d.ts +3 -0
  14. package/dist/types/commands/daemon.d.ts +4 -50
  15. package/dist/types/commands/sdk.d.ts +20 -37
  16. package/dist/types/commands/setup.d.ts +3 -0
  17. package/dist/types/commands/skills.d.ts +9 -0
  18. package/dist/types/config/autorouting-tier-map.d.ts +24 -0
  19. package/dist/types/config/file-lock.d.ts +10 -1
  20. package/dist/types/config/model-profile-activation.d.ts +63 -4
  21. package/dist/types/config/model-registry.d.ts +20 -2
  22. package/dist/types/config/settings-schema.d.ts +11 -0
  23. package/dist/types/coordinator-mcp/question-state.d.ts +6 -1
  24. package/dist/types/coordinator-mcp/server.d.ts +3 -0
  25. package/dist/types/daemon/operator-contract.d.ts +5 -1
  26. package/dist/types/eval/js/worker-protocol.d.ts +2 -0
  27. package/dist/types/extensibility/extensions/types.d.ts +12 -0
  28. package/dist/types/extensibility/gjc-plugins/registry.d.ts +4 -0
  29. package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
  30. package/dist/types/extensibility/shared-events.d.ts +3 -1
  31. package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
  32. package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
  33. package/dist/types/internal-urls/index.d.ts +1 -0
  34. package/dist/types/internal-urls/parse.d.ts +9 -1
  35. package/dist/types/main.d.ts +22 -0
  36. package/dist/types/modes/acp/acp-agent.d.ts +94 -1
  37. package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
  38. package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
  39. package/dist/types/modes/components/session-selector.d.ts +5 -2
  40. package/dist/types/modes/components/thinking-selector.d.ts +2 -1
  41. package/dist/types/modes/components/user-message-selector.d.ts +5 -2
  42. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  43. package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
  44. package/dist/types/modes/interactive-mode.d.ts +1 -1
  45. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
  46. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
  47. package/dist/types/modes/types.d.ts +2 -2
  48. package/dist/types/runtime/process-lifecycle.d.ts +2 -0
  49. package/dist/types/runtime-mcp/manager.d.ts +56 -0
  50. package/dist/types/runtime-mcp/pool.d.ts +17 -0
  51. package/dist/types/runtime-mcp/redaction.d.ts +20 -0
  52. package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
  53. package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
  54. package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
  55. package/dist/types/sdk/acp/adapter.d.ts +27 -0
  56. package/dist/types/sdk/acp/final-text.d.ts +8 -0
  57. package/dist/types/sdk/broker/broker.d.ts +13 -2
  58. package/dist/types/sdk/broker/ensure.d.ts +46 -1
  59. package/dist/types/sdk/broker/identity.d.ts +3 -1
  60. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
  61. package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
  62. package/dist/types/sdk/broker/runtime.d.ts +1 -1
  63. package/dist/types/sdk/broker/session-index.d.ts +22 -3
  64. package/dist/types/sdk/broker/spawn-authority.d.ts +4 -0
  65. package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
  66. package/dist/types/sdk/bus/index.d.ts +19 -0
  67. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
  68. package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
  69. package/dist/types/sdk/cli/master-cli.d.ts +6 -10
  70. package/dist/types/sdk/cli/rows.d.ts +19 -1
  71. package/dist/types/sdk/cli/session-cli.d.ts +22 -5
  72. package/dist/types/sdk/client/client.d.ts +2 -0
  73. package/dist/types/sdk/guides/cli.d.ts +3 -4
  74. package/dist/types/sdk/guides/verify.d.ts +2 -0
  75. package/dist/types/sdk/host/host.d.ts +9 -0
  76. package/dist/types/sdk/host/session-runtime.d.ts +31 -0
  77. package/dist/types/sdk/host/types.d.ts +10 -0
  78. package/dist/types/sdk/index.d.ts +1 -0
  79. package/dist/types/sdk/prompt-deadline-flush.d.ts +83 -0
  80. package/dist/types/sdk/prompt-deadline-manager.d.ts +38 -1
  81. package/dist/types/sdk/prompt-failure.d.ts +30 -1
  82. package/dist/types/sdk/prompt-status.d.ts +8 -0
  83. package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
  84. package/dist/types/sdk/router/replay-retention-gap-cache.d.ts +13 -0
  85. package/dist/types/sdk/router/session-router.d.ts +2 -0
  86. package/dist/types/sdk/startup-capability.d.ts +2 -0
  87. package/dist/types/sdk/transport/relay.d.ts +1 -0
  88. package/dist/types/sdk/transport/serve-cli.d.ts +73 -2
  89. package/dist/types/sdk/transport/stdio.d.ts +5 -1
  90. package/dist/types/session/agent-session.d.ts +168 -21
  91. package/dist/types/session/fallback-chain-controller.d.ts +6 -6
  92. package/dist/types/session/fold-coordinator.d.ts +17 -3
  93. package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
  94. package/dist/types/session/session-manager.d.ts +64 -1
  95. package/dist/types/session/session-work-lease.d.ts +14 -0
  96. package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
  97. package/dist/types/setup/provider-onboarding.d.ts +81 -1
  98. package/dist/types/thinking.d.ts +3 -1
  99. package/dist/types/tools/bash.d.ts +37 -11
  100. package/dist/types/tools/job.d.ts +1 -1
  101. package/dist/types/tools/steer-fold.d.ts +31 -0
  102. package/dist/types/utils/git.d.ts +28 -2
  103. package/dist/types/utils/mupdf-embedded.d.ts +2 -0
  104. package/dist/types/utils/mupdf-wasm-embedded.d.ts +2 -0
  105. package/dist/types/utils/mupdf.d.ts +4 -0
  106. package/dist/types/utils/session-title-generation.d.ts +6 -0
  107. package/dist/types/utils/title-generator.d.ts +11 -2
  108. package/dist/types/web/scrapers/types.d.ts +3 -0
  109. package/package.json +18 -10
  110. package/scripts/build-binary.ts +9 -3
  111. package/scripts/compile-args.ts +0 -9
  112. package/scripts/embed-mupdf.ts +52 -0
  113. package/scripts/generate-sdk-operation-inventory.ts +31 -1
  114. package/scripts/vendor-markit.ts +196 -0
  115. package/src/async/job-manager.ts +60 -9
  116. package/src/cli/daemon-cli.ts +53 -18
  117. package/src/cli/github-release.ts +87 -5
  118. package/src/cli/mcp-cli.ts +38 -4
  119. package/src/cli/public-command-entry.ts +450 -0
  120. package/src/cli/public-command-errors.ts +581 -0
  121. package/src/cli/public-command-evidence.ts +717 -0
  122. package/src/cli/public-command-help.ts +360 -0
  123. package/src/cli/public-command-registry.ts +535 -0
  124. package/src/cli/session-picker.ts +2 -1
  125. package/src/cli/setup-cli.ts +9 -1
  126. package/src/cli/skills-cli.ts +46 -2
  127. package/src/cli.ts +61 -1
  128. package/src/commands/daemon.ts +65 -69
  129. package/src/commands/sdk.ts +232 -100
  130. package/src/commands/setup.ts +2 -0
  131. package/src/commands/skills.ts +11 -0
  132. package/src/config/autorouting-tier-map.ts +16 -0
  133. package/src/config/file-lock.ts +199 -68
  134. package/src/config/model-profile-activation.ts +406 -53
  135. package/src/config/model-registry.ts +204 -25
  136. package/src/config/settings-schema.ts +26 -2
  137. package/src/coordinator-mcp/policy.ts +10 -5
  138. package/src/coordinator-mcp/question-gate-codec.ts +17 -0
  139. package/src/coordinator-mcp/question-state.ts +175 -8
  140. package/src/coordinator-mcp/server.ts +528 -87
  141. package/src/coordinator-mcp/session-reaper.ts +6 -4
  142. package/src/crash/record-loader.ts +6 -2
  143. package/src/daemon/operator-contract.ts +26 -12
  144. package/src/debug/report-bundle.ts +114 -24
  145. package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
  146. package/src/eval/js/context-manager.ts +8 -8
  147. package/src/eval/js/worker-core.ts +9 -4
  148. package/src/eval/js/worker-protocol.ts +2 -0
  149. package/src/extensibility/extensions/runner.ts +52 -4
  150. package/src/extensibility/extensions/types.ts +10 -0
  151. package/src/extensibility/gjc-plugins/registry.ts +196 -37
  152. package/src/extensibility/runtime-skill-discovery.ts +61 -3
  153. package/src/extensibility/shared-events.ts +3 -1
  154. package/src/gjc-runtime/managed-owner-admission.ts +67 -50
  155. package/src/gjc-runtime/session-state-lock.ts +165 -24
  156. package/src/gjc-runtime/ultragoal-succession.ts +31 -3
  157. package/src/internal-urls/docs-index.generated.ts +21 -19
  158. package/src/internal-urls/embedded-protocol.ts +83 -0
  159. package/src/internal-urls/index.ts +1 -0
  160. package/src/internal-urls/parse.ts +21 -1
  161. package/src/internal-urls/router.ts +4 -2
  162. package/src/main.ts +123 -55
  163. package/src/modes/acp/acp-agent.ts +593 -47
  164. package/src/modes/acp/acp-event-mapper.ts +9 -0
  165. package/src/modes/acp/startup-provenance.ts +59 -0
  166. package/src/modes/components/custom-provider-wizard.ts +308 -7
  167. package/src/modes/components/model-selector.ts +31 -15
  168. package/src/modes/components/oauth-selector.ts +9 -0
  169. package/src/modes/components/session-selector.ts +47 -4
  170. package/src/modes/components/status-line/segments.ts +6 -1
  171. package/src/modes/components/thinking-selector.ts +7 -3
  172. package/src/modes/components/user-message-selector.ts +57 -15
  173. package/src/modes/controllers/command-controller.ts +58 -32
  174. package/src/modes/controllers/extension-ui-controller.ts +33 -7
  175. package/src/modes/controllers/input-controller.ts +4 -0
  176. package/src/modes/controllers/selector-controller.ts +177 -18
  177. package/src/modes/interactive-mode.ts +21 -7
  178. package/src/modes/runtime-init.ts +1 -0
  179. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
  180. package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
  181. package/src/modes/types.ts +2 -2
  182. package/src/prompts/agent-fragments/browser-aside.md +1 -0
  183. package/src/prompts/system/title-system.md +1 -1
  184. package/src/prompts/tools/bash.md +1 -1
  185. package/src/prompts/tools/job.md +1 -1
  186. package/src/prompts/tools/read.md +2 -0
  187. package/src/runtime/network-prewarm-service.test.ts +36 -1
  188. package/src/runtime/network-prewarm-service.ts +28 -3
  189. package/src/runtime/process-lifecycle.ts +22 -11
  190. package/src/runtime-mcp/config.ts +16 -1
  191. package/src/runtime-mcp/manager.ts +689 -55
  192. package/src/runtime-mcp/pool.test.ts +31 -0
  193. package/src/runtime-mcp/pool.ts +304 -28
  194. package/src/runtime-mcp/redaction.ts +42 -0
  195. package/src/runtime-mcp/startup-policy.ts +7 -0
  196. package/src/runtime-mcp/tool-bridge.ts +71 -0
  197. package/src/runtime-mcp/transports/http.ts +108 -25
  198. package/src/sdk/acp/adapter.ts +90 -9
  199. package/src/sdk/acp/final-text.ts +19 -2
  200. package/src/sdk/broker/broker.ts +378 -52
  201. package/src/sdk/broker/ensure.ts +97 -19
  202. package/src/sdk/broker/identity.ts +9 -1
  203. package/src/sdk/broker/lifecycle-ledger.ts +95 -31
  204. package/src/sdk/broker/lifecycle.ts +690 -141
  205. package/src/sdk/broker/runtime.ts +1 -1
  206. package/src/sdk/broker/session-index.ts +97 -4
  207. package/src/sdk/broker/spawn-authority.ts +35 -7
  208. package/src/sdk/broker/stderr-drainer.ts +59 -0
  209. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  210. package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
  211. package/src/sdk/bus/index.ts +721 -84
  212. package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
  213. package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
  214. package/src/sdk/bus/telegram-daemon.ts +10 -3
  215. package/src/sdk/cli/master-cli.ts +47 -36
  216. package/src/sdk/cli/rows.ts +30 -7
  217. package/src/sdk/cli/session-cli.ts +687 -184
  218. package/src/sdk/client/client.ts +18 -0
  219. package/src/sdk/guides/catalog.ts +2 -2
  220. package/src/sdk/guides/cli.ts +20 -62
  221. package/src/sdk/guides/verify.ts +14 -0
  222. package/src/sdk/host/host.ts +49 -5
  223. package/src/sdk/host/session-runtime.test.ts +636 -9
  224. package/src/sdk/host/session-runtime.ts +499 -118
  225. package/src/sdk/host/types.ts +12 -1
  226. package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
  227. package/src/sdk/host/websocket-transport.ts +40 -6
  228. package/src/sdk/index.ts +11 -0
  229. package/src/sdk/prompt-deadline-flush.ts +334 -0
  230. package/src/sdk/prompt-deadline-manager.ts +136 -4
  231. package/src/sdk/prompt-failure.ts +43 -1
  232. package/src/sdk/prompt-status.ts +8 -0
  233. package/src/sdk/prompt-tool-boundary.ts +121 -0
  234. package/src/sdk/protocol/operation-inventory.generated.json +106 -0
  235. package/src/sdk/router/replay-retention-gap-cache.ts +27 -0
  236. package/src/sdk/router/session-router.ts +31 -4
  237. package/src/sdk/session.ts +194 -16
  238. package/src/sdk/startup-capability.ts +1 -1
  239. package/src/sdk/transport/relay.ts +71 -6
  240. package/src/sdk/transport/serve-cli.ts +494 -49
  241. package/src/sdk/transport/socket.ts +12 -11
  242. package/src/sdk/transport/stdio.ts +7 -3
  243. package/src/session/agent-session.ts +2437 -400
  244. package/src/session/fallback-chain-controller.ts +6 -7
  245. package/src/session/fold-coordinator.ts +24 -5
  246. package/src/session/internal/managed-session-scope.ts +58 -18
  247. package/src/session/internal/managed-session-storage.ts +304 -11
  248. package/src/session/session-manager.ts +382 -44
  249. package/src/session/session-work-lease.ts +34 -0
  250. package/src/session-import/codex.ts +12 -3
  251. package/src/setup/model-onboarding-guidance.ts +1 -1
  252. package/src/setup/provider-onboarding.ts +397 -24
  253. package/src/slash-commands/builtin-registry.ts +258 -14
  254. package/src/task/index.ts +14 -2
  255. package/src/thinking.ts +21 -2
  256. package/src/tools/bash.ts +98 -78
  257. package/src/tools/fetch.ts +119 -20
  258. package/src/tools/job.ts +151 -25
  259. package/src/tools/output-meta.ts +15 -2
  260. package/src/tools/path-utils.ts +27 -3
  261. package/src/tools/read.ts +3 -3
  262. package/src/tools/steer-fold.ts +107 -0
  263. package/src/tools/tool-catalog.generated.ts +29 -2
  264. package/src/tools/tool-errors.ts +4 -0
  265. package/src/utils/git.ts +53 -3
  266. package/src/utils/markit.ts +55 -41
  267. package/src/utils/mupdf-assets.d.ts +4 -0
  268. package/src/utils/mupdf-embedded.ts +4 -0
  269. package/src/utils/mupdf-wasm-embedded.ts +3 -0
  270. package/src/utils/mupdf-wasm.ts +24 -2
  271. package/src/utils/mupdf.ts +129 -0
  272. package/src/utils/session-title-generation.ts +29 -0
  273. package/src/utils/title-generator.ts +46 -2
  274. package/src/web/scrapers/types.ts +24 -3
  275. package/vendor/markit-ai/LICENSE +21 -0
  276. package/vendor/markit-ai/MANIFEST.json +412 -0
  277. package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
  278. package/vendor/markit-ai/dist/commands/config.js +139 -0
  279. package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
  280. package/vendor/markit-ai/dist/commands/convert.js +113 -0
  281. package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
  282. package/vendor/markit-ai/dist/commands/formats.js +92 -0
  283. package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
  284. package/vendor/markit-ai/dist/commands/init.js +29 -0
  285. package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
  286. package/vendor/markit-ai/dist/commands/onboard.js +61 -0
  287. package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
  288. package/vendor/markit-ai/dist/commands/plugin.js +58 -0
  289. package/vendor/markit-ai/dist/config.d.ts +26 -0
  290. package/vendor/markit-ai/dist/config.js +42 -0
  291. package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
  292. package/vendor/markit-ai/dist/converters/audio.js +104 -0
  293. package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
  294. package/vendor/markit-ai/dist/converters/csv.js +83 -0
  295. package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
  296. package/vendor/markit-ai/dist/converters/docx.js +57 -0
  297. package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
  298. package/vendor/markit-ai/dist/converters/epub.js +128 -0
  299. package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
  300. package/vendor/markit-ai/dist/converters/github.js +148 -0
  301. package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
  302. package/vendor/markit-ai/dist/converters/html.js +30 -0
  303. package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
  304. package/vendor/markit-ai/dist/converters/image.js +124 -0
  305. package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
  306. package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
  307. package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
  308. package/vendor/markit-ai/dist/converters/iwork.js +391 -0
  309. package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
  310. package/vendor/markit-ai/dist/converters/json.js +21 -0
  311. package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
  312. package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
  313. package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
  314. package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
  315. package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
  316. package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
  317. package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
  318. package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
  319. package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
  320. package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
  321. package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
  322. package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
  323. package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
  324. package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
  325. package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
  326. package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
  327. package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
  328. package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
  329. package/vendor/markit-ai/dist/converters/pptx.js +289 -0
  330. package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
  331. package/vendor/markit-ai/dist/converters/rss.js +138 -0
  332. package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
  333. package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
  334. package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
  335. package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
  336. package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
  337. package/vendor/markit-ai/dist/converters/xml.js +18 -0
  338. package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
  339. package/vendor/markit-ai/dist/converters/yaml.js +17 -0
  340. package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
  341. package/vendor/markit-ai/dist/converters/zip.js +57 -0
  342. package/vendor/markit-ai/dist/index.d.ts +30 -0
  343. package/vendor/markit-ai/dist/index.js +26 -0
  344. package/vendor/markit-ai/dist/main.d.ts +2 -0
  345. package/vendor/markit-ai/dist/main.js +191 -0
  346. package/vendor/markit-ai/dist/markit.d.ts +45 -0
  347. package/vendor/markit-ai/dist/markit.js +267 -0
  348. package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
  349. package/vendor/markit-ai/dist/plugins/api.js +42 -0
  350. package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
  351. package/vendor/markit-ai/dist/plugins/index.js +3 -0
  352. package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
  353. package/vendor/markit-ai/dist/plugins/installer.js +176 -0
  354. package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
  355. package/vendor/markit-ai/dist/plugins/loader.js +61 -0
  356. package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
  357. package/vendor/markit-ai/dist/plugins/types.js +1 -0
  358. package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
  359. package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
  360. package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
  361. package/vendor/markit-ai/dist/providers/index.js +56 -0
  362. package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
  363. package/vendor/markit-ai/dist/providers/openai.js +76 -0
  364. package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
  365. package/vendor/markit-ai/dist/providers/types.js +1 -0
  366. package/vendor/markit-ai/dist/types.d.ts +36 -0
  367. package/vendor/markit-ai/dist/types.js +1 -0
  368. package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
  369. package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
  370. package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
  371. package/vendor/markit-ai/dist/utils/output.js +31 -0
  372. package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
  373. package/vendor/markit-ai/dist/utils/turndown.js +70 -0
  374. package/vendor/markit-ai/package.json +74 -0
  375. package/vendor/markit-ai.patch +49 -0
@@ -1,5 +1,11 @@
1
1
  import { ThinkingLevel } from "@gajae-code/agent-core";
2
- import { getSupportedEfforts, type Model, modelSupportsServiceTier, modelsAreEqual } from "@gajae-code/ai/core";
2
+ import {
3
+ getMiniMaxThinkingMode,
4
+ getSupportedEfforts,
5
+ type Model,
6
+ modelSupportsServiceTier,
7
+ modelsAreEqual,
8
+ } from "@gajae-code/ai/core";
3
9
  import {
4
10
  Container,
5
11
  fuzzyFilter,
@@ -26,6 +32,7 @@ import type { BillingPath } from "../../config/billing-path";
26
32
  import {
27
33
  getProxyRoutableProviders,
28
34
  inspectProxyProviderId,
35
+ isModelProfileProxyConfigured,
29
36
  requiresQualifiedModelProfileRoleResolution,
30
37
  resolveProxyMode,
31
38
  rewriteSelectorForProxy,
@@ -426,6 +433,7 @@ export class ModelSelectorComponent extends Container {
426
433
  #presetScopeMenuOpen: boolean = false;
427
434
  #presetScopeIndex: number = 0;
428
435
  #providerAuthById = new Map<string, boolean>();
436
+ #credentiallessProviders = new Set<string>();
429
437
  #bareProfileAuthByName = new Map<string, boolean>();
430
438
  // Rebuilt with the provider and bare-selector snapshots on every catalog or credential refresh.
431
439
  #profileAuthByName = new Map<string, boolean>();
@@ -1441,17 +1449,16 @@ export class ModelSelectorComponent extends Container {
1441
1449
  const proxyProvider = tryResolveProxyProviderId(this.#settings);
1442
1450
  if (
1443
1451
  proxyProvider !== undefined &&
1444
- !(this.#modelRegistry.getConfiguredProviderIds?.() ?? []).includes(proxyProvider)
1452
+ !isModelProfileProxyConfigured(
1453
+ proxyProvider,
1454
+ this.#modelRegistry.getConfiguredProviderIds?.(),
1455
+ this.#credentiallessProviders.has(proxyProvider),
1456
+ )
1445
1457
  )
1446
1458
  return false;
1447
1459
  try {
1448
1460
  if (resolveProxyMode(this.#settings) === "always") {
1449
- const configuredProviders = this.#modelRegistry.getConfiguredProviderIds?.() ?? [];
1450
- if (
1451
- proxyProvider === undefined ||
1452
- this.#isProviderAuthenticated(proxyProvider) !== true ||
1453
- !configuredProviders.includes(proxyProvider)
1454
- )
1461
+ if (proxyProvider === undefined || this.#isProviderAuthenticated(proxyProvider) !== true)
1455
1462
  return false;
1456
1463
  }
1457
1464
  } catch {
@@ -1530,6 +1537,7 @@ export class ModelSelectorComponent extends Container {
1530
1537
  async #refreshProviderAuth(): Promise<void> {
1531
1538
  const refreshGeneration = ++this.#providerAuthRefreshGeneration;
1532
1539
  this.#providerAuthById = new Map();
1540
+ this.#credentiallessProviders = new Set();
1533
1541
  this.#bareProfileAuthByName = new Map();
1534
1542
  this.#profileAuthByName = new Map();
1535
1543
  const availableModels = this.#getProfileAvailableModels();
@@ -1586,9 +1594,9 @@ export class ModelSelectorComponent extends Container {
1586
1594
  [...providers].map(async provider => {
1587
1595
  try {
1588
1596
  const apiKey = await this.#modelRegistry.getApiKeyForProvider(provider, this.#authSessionId);
1589
- return [provider, apiKey === kNoAuth || isAuthenticated(apiKey)] as const;
1597
+ return [provider, apiKey === kNoAuth || isAuthenticated(apiKey), apiKey === kNoAuth] as const;
1590
1598
  } catch {
1591
- return [provider, false] as const;
1599
+ return [provider, false, false] as const;
1592
1600
  }
1593
1601
  }),
1594
1602
  );
@@ -1598,7 +1606,10 @@ export class ModelSelectorComponent extends Container {
1598
1606
  refreshGeneration !== this.#providerAuthRefreshGeneration
1599
1607
  )
1600
1608
  return;
1601
- this.#providerAuthById = new Map(entries);
1609
+ this.#providerAuthById = new Map(entries.map(([provider, authenticated]) => [provider, authenticated]));
1610
+ this.#credentiallessProviders = new Set(
1611
+ entries.filter(([, , keyless]) => keyless).map(([provider]) => provider),
1612
+ );
1602
1613
  const profileAuthEntries = await Promise.all(
1603
1614
  [...this.#getPresetGroups().values()].flat().map(async profile => {
1604
1615
  const bindings = resolveProfileBindings(profile);
@@ -1853,7 +1864,7 @@ export class ModelSelectorComponent extends Container {
1853
1864
  const modelLabel = sanitizeText(`${model.provider}/${model.id}`).replace(/\s+/g, " ").trim();
1854
1865
  let label = modelLabel;
1855
1866
  if (thinkingLevel && thinkingLevel !== ThinkingLevel.Inherit) {
1856
- label += ` (${getThinkingLevelMetadata(thinkingLevel).label})`;
1867
+ label += ` (${getThinkingLevelMetadata(thinkingLevel, model).label})`;
1857
1868
  }
1858
1869
  const billing = this.#billingPathForModel(model)?.label;
1859
1870
  if (billing) label += ` [${billing}]`;
@@ -2214,7 +2225,7 @@ export class ModelSelectorComponent extends Container {
2214
2225
  const assigned = this.#roles[role];
2215
2226
  if (roleInfo.tag && assigned && modelsAreEqual(assigned.model, item.model)) {
2216
2227
  const badge = makeInvertedBadge(roleInfo.tag, roleInfo.color ?? "muted");
2217
- const thinkingLabel = getThinkingLevelMetadata(assigned.thinkingLevel).label;
2228
+ const thinkingLabel = getThinkingLevelMetadata(assigned.thinkingLevel, item.model).label;
2218
2229
 
2219
2230
  // Subagent roles (task.agentModelOverrides) run under task.serviceTier,
2220
2231
  // so their ⚡ uses the effective subagent tier. A non-subagent
@@ -2326,6 +2337,11 @@ export class ModelSelectorComponent extends Container {
2326
2337
  #renderActionMenu(item: ModelItem | CanonicalModelItem): void {
2327
2338
  this.#listContainer.addChild(new Spacer(1));
2328
2339
  this.#listContainer.addChild(new Text(theme.fg("muted", ` Action for: ${item.model.id}`), 0, 0));
2340
+ if (getMiniMaxThinkingMode(item.model) === "always-on") {
2341
+ this.#listContainer.addChild(
2342
+ new Text(theme.fg("muted", " Reasoning: always on; effort control is unsupported"), 0, 0),
2343
+ );
2344
+ }
2329
2345
  this.#listContainer.addChild(new Spacer(1));
2330
2346
  const actionCount = this.#getActionCount(item.model);
2331
2347
  for (let i = 0; i < actionCount; i++) {
@@ -2390,7 +2406,7 @@ export class ModelSelectorComponent extends Container {
2390
2406
  // Show the highlighted reasoning level — never the model id (that mislabel
2391
2407
  // made "Reasoning for Default: gpt-5.6-luna" look like a level).
2392
2408
  const selectedLevel = choice.levels[this.#selectedThinkingIndex];
2393
- const selectedLevelLabel = selectedLevel ? getThinkingLevelMetadata(selectedLevel).label : "—";
2409
+ const selectedLevelLabel = selectedLevel ? getThinkingLevelMetadata(selectedLevel, choice.item.model).label : "—";
2394
2410
  this.#listContainer.addChild(new Spacer(1));
2395
2411
  this.#listContainer.addChild(
2396
2412
  new Text(theme.fg("muted", ` Reasoning for ${targetLabel}: ${selectedLevelLabel}`), 0, 0),
@@ -2398,7 +2414,7 @@ export class ModelSelectorComponent extends Container {
2398
2414
  this.#listContainer.addChild(new Spacer(1));
2399
2415
  for (let i = 0; i < choice.levels.length; i++) {
2400
2416
  const level = choice.levels[i];
2401
- const metadata = getThinkingLevelMetadata(level);
2417
+ const metadata = getThinkingLevelMetadata(level, choice.item.model);
2402
2418
  const prefix = i === this.#selectedThinkingIndex ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
2403
2419
  const label = `${metadata.label} — ${metadata.description}`;
2404
2420
  this.#listContainer.addChild(
@@ -164,6 +164,15 @@ export class OAuthSelectorComponent extends Container {
164
164
  { kind: "auto", selectable: true },
165
165
  { kind: "add", selectable: this.#onAddAccount !== undefined },
166
166
  );
167
+ const firstAccount = this.#accountEntries.find(entry => entry.kind === "account");
168
+ if (firstAccount && !firstAccount.selectable) {
169
+ const addAccountIndex =
170
+ firstAccount.row.disabled && this.#onAddAccount !== undefined
171
+ ? this.#accountEntries.findIndex(entry => entry.kind === "add" && entry.selectable)
172
+ : -1;
173
+ this.#selectedIndex =
174
+ addAccountIndex >= 0 ? addAccountIndex : this.#accountEntries.findIndex(entry => entry.selectable);
175
+ }
167
176
  } else if (this.#accountEntries.some(entry => entry.kind === "account" && entry.selectable)) {
168
177
  this.#accountEntries.push({ kind: "all", selectable: this.#onAccountRemove !== undefined });
169
178
  }
@@ -39,6 +39,7 @@ export type SessionInspector = (sessionPath: string) => Promise<ResumeTailInspec
39
39
 
40
40
  type SelectorState =
41
41
  | { kind: "browsing" }
42
+ | { kind: "starring"; token: number }
42
43
  | { kind: "checking"; session: SessionInfo; token: number }
43
44
  | { kind: "confirming"; session: SessionInfo; token: number; identity: ResumeSessionIdentity }
44
45
  | { kind: "settled" };
@@ -52,12 +53,13 @@ class SessionList implements Component {
52
53
  onCancel?: () => void;
53
54
  onExit: () => void = () => {};
54
55
  onDeleteRequest?: (session: SessionInfo) => void;
56
+ onToggleStarRequest?: (session: SessionInfo) => void;
55
57
 
56
58
  constructor(
57
59
  private readonly allSessions: SessionInfo[],
58
60
  private readonly showCwd = false,
59
61
  ) {
60
- this.#filteredSessions = allSessions;
62
+ this.#filteredSessions = prioritizeStarredSessions(allSessions);
61
63
  this.#searchInput.onSubmit = () => {
62
64
  const selected = this.#filteredSessions[this.#selectedIndex];
63
65
  if (selected) this.onSelect?.(selected);
@@ -100,6 +102,17 @@ class SessionList implements Component {
100
102
  this.#filterSessions(this.#searchInput.getValue());
101
103
  }
102
104
 
105
+ setSessionStarred(sessionPath: string, starred: boolean): void {
106
+ const session = this.allSessions.find(session => session.path === sessionPath);
107
+ if (!session) return;
108
+ session.starred = starred;
109
+ this.#filterSessions(this.#searchInput.getValue());
110
+ this.#selectedIndex = Math.max(
111
+ 0,
112
+ this.#filteredSessions.findIndex(session => session.path === sessionPath),
113
+ );
114
+ }
115
+
103
116
  invalidate(): void {}
104
117
 
105
118
  render(width: number): string[] {
@@ -155,6 +168,8 @@ class SessionList implements Component {
155
168
  }
156
169
  if (start > 0 || end < this.#filteredSessions.length)
157
170
  lines.push(theme.fg("muted", ` (${this.#selectedIndex + 1}/${this.#filteredSessions.length})`));
171
+ if (this.onToggleStarRequest)
172
+ lines.push(theme.fg("muted", truncateToWidth(" [Ctrl+S to star/unstar selected session]", width)));
158
173
  lines.push(
159
174
  "",
160
175
  theme.fg("muted", " [Del to delete selected transcript/artifacts, Enter to select, Esc to cancel]"),
@@ -177,7 +192,10 @@ class SessionList implements Component {
177
192
  this.#selectedIndex += delta;
178
193
  this.#clampSelectedIndex();
179
194
  };
180
- if (matchesKey(keyData, "delete")) {
195
+ if (matchesKey(keyData, "ctrl+s")) {
196
+ const selected = this.#filteredSessions[this.#selectedIndex];
197
+ if (selected) this.onToggleStarRequest?.(selected);
198
+ } else if (matchesKey(keyData, "delete")) {
181
199
  const selected = this.#filteredSessions[this.#selectedIndex];
182
200
  if (selected) this.onDeleteRequest?.(selected);
183
201
  } else if (matchesKey(keyData, "up")) move(-1);
@@ -194,7 +212,7 @@ class SessionList implements Component {
194
212
  }
195
213
  }
196
214
 
197
- /** A one-shot resume consent selector. It never opens or mutates sessions. */
215
+ /** A one-shot resume consent selector with host-owned deletion and star persistence. */
198
216
  export class SessionSelectorComponent extends Container {
199
217
  #sessionList: SessionList;
200
218
  #confirmationDialog: HookSelectorComponent | null = null;
@@ -211,6 +229,7 @@ export class SessionSelectorComponent extends Container {
211
229
  private readonly onDelete?: (session: SessionInfo) => Promise<boolean>,
212
230
  private readonly inspector?: SessionInspector,
213
231
  private readonly onSelection?: (selection: SessionSelectionResult) => void,
232
+ private readonly onSetStarred?: (session: SessionInfo, starred: boolean) => Promise<void>,
214
233
  ) {
215
234
  super();
216
235
  this.addChild(new Spacer(1));
@@ -219,7 +238,7 @@ export class SessionSelectorComponent extends Container {
219
238
  this.addChild(new DynamicBorder());
220
239
  this.addChild(new Spacer(1));
221
240
  this.addChild(this.#messageContainer);
222
- this.#sessionList = new SessionList(prioritizeStarredSessions(sessions));
241
+ this.#sessionList = new SessionList([...sessions]);
223
242
  this.#sessionList.onSelect = session => {
224
243
  if (this.inspector) void this.#inspect(session);
225
244
  else this.#dispatchSelect(session.path);
@@ -227,6 +246,7 @@ export class SessionSelectorComponent extends Container {
227
246
  this.#sessionList.onCancel = () => this.#cancel();
228
247
  this.#sessionList.onExit = () => this.#exit();
229
248
  this.#sessionList.onDeleteRequest = session => this.#showDeleteConfirmation(session);
249
+ if (this.onSetStarred) this.#sessionList.onToggleStarRequest = session => void this.#toggleStar(session);
230
250
  this.addChild(this.#sessionList);
231
251
  this.addChild(new Spacer(1));
232
252
  this.addChild(new DynamicBorder());
@@ -294,6 +314,29 @@ export class SessionSelectorComponent extends Container {
294
314
  this.#sessionList.setInputFrozen(true);
295
315
  this.onExit();
296
316
  }
317
+ async #toggleStar(session: SessionInfo): Promise<void> {
318
+ if (!this.onSetStarred || this.#state.kind !== "browsing" || this.#confirmationDialog) return;
319
+ const token = ++this.#nextToken;
320
+ const starred = !session.starred;
321
+ this.#state = { kind: "starring", token };
322
+ this.#sessionList.setInputFrozen(true);
323
+ this.#clearError();
324
+ this.#requestRender();
325
+ try {
326
+ await this.onSetStarred(session, starred);
327
+ if (this.#state.kind !== "starring" || this.#state.token !== token) return;
328
+ this.#sessionList.setSessionStarred(session.path, starred);
329
+ } catch (error) {
330
+ if (this.#state.kind !== "starring" || this.#state.token !== token) return;
331
+ this.#showError(error instanceof Error ? error.message : String(error));
332
+ } finally {
333
+ if (this.#state.kind === "starring" && this.#state.token === token) {
334
+ this.#state = { kind: "browsing" };
335
+ this.#sessionList.setInputFrozen(false);
336
+ this.#requestRender();
337
+ }
338
+ }
339
+ }
297
340
  async #inspect(session: SessionInfo): Promise<void> {
298
341
  const inspector = this.inspector;
299
342
  if (!inspector || this.#state.kind !== "browsing") return;
@@ -1,9 +1,11 @@
1
1
  import * as os from "node:os";
2
2
  import * as path from "node:path";
3
3
  import { ThinkingLevel } from "@gajae-code/agent-core";
4
+ import { getMiniMaxThinkingMode } from "@gajae-code/ai/model-thinking";
4
5
  import { TERMINAL, truncateToWidth } from "@gajae-code/tui";
5
6
  import { formatDuration, formatNumber, getProjectDir, pathIsWithin, relativePathWithinRoot } from "@gajae-code/utils";
6
7
  import { type ThemeColor, theme } from "../../../modes/theme/theme";
8
+ import { getThinkingLevelMetadata } from "../../../thinking";
7
9
  import { shortenPath } from "../../../tools/render-utils";
8
10
  import { getSessionAccentAnsi, getSessionAccentHex } from "../../../utils/session-color";
9
11
  import { sanitizeStatusText } from "../../shared";
@@ -113,7 +115,10 @@ const modelSegment: StatusLineSegment = {
113
115
  }
114
116
 
115
117
  // Add thinking level with dot separator
116
- if (opts.showThinkingLevel !== false && state.model?.thinking) {
118
+ const miniMaxMode = state.model && getMiniMaxThinkingMode(state.model);
119
+ if (opts.showThinkingLevel !== false && miniMaxMode) {
120
+ content += `${theme.sep.dot}${getThinkingLevelMetadata(state.thinkingLevel ?? ThinkingLevel.Off, state.model).label}`;
121
+ } else if (opts.showThinkingLevel !== false && state.model?.thinking) {
117
122
  const level = state.thinkingLevel ?? ThinkingLevel.Off;
118
123
  if (level !== ThinkingLevel.Off) {
119
124
  const thinkingText = theme.thinking[level as keyof typeof theme.thinking];
@@ -1,7 +1,8 @@
1
1
  import { ThinkingLevel, type ThinkingLevel as ThinkingLevelValue } from "@gajae-code/agent-core";
2
+ import type { Model } from "@gajae-code/ai/types";
2
3
  import { Container, type SelectItem, SelectList, Text } from "@gajae-code/tui";
3
4
  import { getSelectListTheme, theme } from "../../modes/theme/theme";
4
- import { getThinkingLevelMetadata, type ThinkingLevelValue as ThinkingMetadataValue } from "../../thinking-metadata";
5
+ import { getThinkingLevelMetadata } from "../../thinking";
5
6
  import { DynamicBorder } from "./dynamic-border";
6
7
 
7
8
  const SCOPE_ITEMS = [
@@ -31,21 +32,24 @@ export class ThinkingSelectorComponent extends Container {
31
32
  #selectedLevel: ThinkingLevelValue | undefined;
32
33
  readonly #onSelect: (selection: ThinkingSelectorSelection) => void;
33
34
  readonly #onCancel: () => void;
35
+ readonly #model: Model | undefined;
34
36
 
35
37
  constructor(
36
38
  currentLevel: ThinkingLevelValue | undefined,
37
39
  availableLevels: ThinkingLevelValue[],
38
40
  onSelect: (selection: ThinkingSelectorSelection) => void,
39
41
  onCancel: () => void,
42
+ model?: Model,
40
43
  ) {
41
44
  super();
42
45
 
43
46
  this.#onSelect = onSelect;
44
47
  this.#onCancel = onCancel;
48
+ this.#model = model;
45
49
 
46
50
  const currentValue = currentLevel ?? ThinkingLevel.Off;
47
51
  const thinkingLevels: SelectItem[] = availableLevels.map(level => {
48
- const metadata = getThinkingLevelMetadata(level as ThinkingMetadataValue);
52
+ const metadata = getThinkingLevelMetadata(level, model);
49
53
  return level === currentValue
50
54
  ? { ...metadata, label: metadata.label + theme.fg("muted", " (current)") }
51
55
  : metadata;
@@ -96,7 +100,7 @@ export class ThinkingSelectorComponent extends Container {
96
100
  this.#renderLevelList();
97
101
  };
98
102
 
99
- const metadata = getThinkingLevelMetadata(level as ThinkingMetadataValue);
103
+ const metadata = getThinkingLevelMetadata(level, this.#model);
100
104
  this.detachAll();
101
105
  this.#selectList = scopeList;
102
106
  this.addChild(new DynamicBorder());
@@ -14,9 +14,10 @@ interface UserMessageItem {
14
14
  */
15
15
  class UserMessageList implements Component {
16
16
  #selectedIndex: number = 0;
17
+ #selectionInFlight = false;
17
18
  onSelect?: (entryId: string) => void;
18
19
  onCancel?: () => void;
19
- #maxVisible: number = 10; // Max messages visible
20
+ #availableRows = Number.POSITIVE_INFINITY;
20
21
 
21
22
  constructor(private readonly messages: UserMessageItem[]) {
22
23
  // Store messages in chronological order (oldest to newest)
@@ -24,6 +25,10 @@ class UserMessageList implements Component {
24
25
  this.#selectedIndex = Math.max(0, this.messages.length - 1);
25
26
  }
26
27
 
28
+ setAvailableRows(rows: number): void {
29
+ this.#availableRows = Math.max(3, rows);
30
+ }
31
+
27
32
  invalidate(): void {
28
33
  // No cached state to invalidate currently
29
34
  }
@@ -36,12 +41,15 @@ class UserMessageList implements Component {
36
41
  return lines;
37
42
  }
38
43
 
39
- // Calculate visible range with scrolling
44
+ // Each entry uses two content rows plus a separator. Leave one row for
45
+ // the scroll position whenever the full history does not fit.
46
+ const needsScrollIndicator = this.messages.length * 3 > this.#availableRows;
47
+ const maxVisible = Math.max(1, Math.floor((this.#availableRows - (needsScrollIndicator ? 1 : 0)) / 3));
40
48
  const startIndex = Math.max(
41
49
  0,
42
- Math.min(this.#selectedIndex - Math.floor(this.#maxVisible / 2), this.messages.length - this.#maxVisible),
50
+ Math.min(this.#selectedIndex - Math.floor(maxVisible / 2), this.messages.length - maxVisible),
43
51
  );
44
- const endIndex = Math.min(startIndex + this.#maxVisible, this.messages.length);
52
+ const endIndex = Math.min(startIndex + maxVisible, this.messages.length);
45
53
 
46
54
  // Render visible messages (2 lines per message + blank line)
47
55
  for (let i = startIndex; i < endIndex; i++) {
@@ -77,6 +85,7 @@ class UserMessageList implements Component {
77
85
  }
78
86
 
79
87
  handleInput(keyData: string): void {
88
+ if (this.#selectionInFlight) return;
80
89
  // Up arrow - go to previous (older) message, wrap to bottom when at top
81
90
  if (matchesKey(keyData, "up")) {
82
91
  this.#selectedIndex = this.#selectedIndex === 0 ? this.messages.length - 1 : this.#selectedIndex - 1;
@@ -89,6 +98,7 @@ class UserMessageList implements Component {
89
98
  else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
90
99
  const selected = this.messages[this.#selectedIndex];
91
100
  if (selected && this.onSelect) {
101
+ this.#selectionInFlight = true;
92
102
  this.onSelect(selected.id);
93
103
  }
94
104
  }
@@ -102,21 +112,43 @@ class UserMessageList implements Component {
102
112
  }
103
113
 
104
114
  /**
105
- * Component that renders a user message selector for branching
115
+ * Component that renders a prompt selector for creating a new session
106
116
  */
107
117
  export class UserMessageSelectorComponent extends Container {
108
118
  #messageList: UserMessageList;
109
-
110
- constructor(messages: UserMessageItem[], onSelect: (entryId: string) => void, onCancel: () => void) {
119
+ #header: Container;
120
+ #footer: Container;
121
+
122
+ constructor(
123
+ messages: UserMessageItem[],
124
+ onSelect: (entryId: string) => void,
125
+ onCancel: () => void,
126
+ private readonly getViewportRows: () => number,
127
+ ) {
111
128
  super();
112
129
 
113
130
  // Add header
114
- this.addChild(new Spacer(1));
115
- this.addChild(new Text(theme.bold("Branch from Message"), 1, 0));
116
- this.addChild(new Text(theme.fg("muted", "Select a message to create a new branch from that point"), 1, 0));
117
- this.addChild(new Spacer(1));
118
- this.addChild(new DynamicBorder());
119
- this.addChild(new Spacer(1));
131
+ this.#header = new Container();
132
+ this.#header.addChild(new Spacer(1));
133
+ this.#header.addChild(new Text(theme.bold("Fork from Prompt"), 1, 0));
134
+ this.#header.addChild(
135
+ new Text(
136
+ theme.fg(
137
+ "muted",
138
+ "Creates a new session with history before the prompt; shared files stay in the same cwd.",
139
+ ),
140
+ 1,
141
+ 0,
142
+ ),
143
+ );
144
+ this.#header.addChild(
145
+ new Text(theme.fg("muted", "The selected prompt is restored for editing, not submitted."), 1, 0),
146
+ );
147
+ this.#header.addChild(new Text(theme.fg("muted", "↑/↓ move · Enter select · Esc cancel"), 1, 0));
148
+ this.#header.addChild(new Spacer(1));
149
+ this.#header.addChild(new DynamicBorder());
150
+ this.#header.addChild(new Spacer(1));
151
+ this.addChild(this.#header);
120
152
 
121
153
  // Create message list
122
154
  this.#messageList = new UserMessageList(messages);
@@ -126,8 +158,10 @@ export class UserMessageSelectorComponent extends Container {
126
158
  this.addChild(this.#messageList);
127
159
 
128
160
  // Add bottom border
129
- this.addChild(new Spacer(1));
130
- this.addChild(new DynamicBorder());
161
+ this.#footer = new Container();
162
+ this.#footer.addChild(new Spacer(1));
163
+ this.#footer.addChild(new DynamicBorder());
164
+ this.addChild(this.#footer);
131
165
 
132
166
  // Auto-cancel if no messages
133
167
  if (messages.length === 0) {
@@ -135,6 +169,14 @@ export class UserMessageSelectorComponent extends Container {
135
169
  }
136
170
  }
137
171
 
172
+ render(width: number): string[] {
173
+ const chromeRows = this.#header.render(width).length + this.#footer.render(width).length;
174
+ // Reserve the pinned status/composer rows outside the overlay. Recompute
175
+ // against current terminal rows and wrapped header height on every render.
176
+ this.#messageList.setAvailableRows(this.getViewportRows() - chromeRows - 2);
177
+ return super.render(width);
178
+ }
179
+
138
180
  getMessageList(): UserMessageList {
139
181
  return this.#messageList;
140
182
  }
@@ -38,7 +38,16 @@ import { replaceTabs } from "../../tools/render-utils";
38
38
  import { getDisplayChangelogEntries } from "../../utils/changelog";
39
39
  import { copyToClipboard } from "../../utils/clipboard";
40
40
  import { openPath } from "../../utils/open";
41
- import { setSessionTerminalTitle } from "../../utils/title-generator";
41
+ import {
42
+ beginSessionTitleGeneration,
43
+ invalidateSessionTitleGeneration,
44
+ isSessionTitleGenerationCurrent,
45
+ } from "../../utils/session-title-generation";
46
+ import {
47
+ buildConversationTitleInput,
48
+ generateSessionTitle,
49
+ setSessionTerminalTitle,
50
+ } from "../../utils/title-generator";
42
51
  import { addChatChild, prepareTranscriptRebuild, syncPendingExecutionComponents } from "../utils/ui-helpers";
43
52
 
44
53
  type HindsightModule = typeof import("../../hindsight");
@@ -84,6 +93,7 @@ export function buildHelpMarkdown(keybindings: Pick<KeybindingsManager, "getAcce
84
93
  "| Resume another session | `/resume` |",
85
94
  "| Show session details | `/session info` |",
86
95
  "| Delete current session transcript/artifacts | `/session delete` |",
96
+ "| Fork from an earlier prompt into a new session | `/fork` |",
87
97
  `| Select a model | \`/model\` or ${selectModelKey} |`,
88
98
  `| Queue a message for the next turn | ${queueMessageKey} |`,
89
99
  `| Select or edit a queued message | ${dequeueMessageKey} |`,
@@ -1109,31 +1119,7 @@ export class CommandController {
1109
1119
  }
1110
1120
 
1111
1121
  async handleForkCommand(): Promise<void> {
1112
- if (this.ctx.session.isStreaming) {
1113
- this.ctx.showWarning("Wait for the current response to finish or abort it before forking.");
1114
- return;
1115
- }
1116
-
1117
- const success = await this.ctx.session.fork();
1118
- if (this.ctx.isStopped?.()) return;
1119
- if (!success) {
1120
- this.ctx.showError("Fork failed (session not persisted or cancelled)");
1121
- return;
1122
- }
1123
- clearInteractiveActivityLoaders(this.ctx);
1124
- stopInteractiveActivityIndicator(this.ctx, { foregroundSettled: true });
1125
- this.ctx.resetIrcSidebarSession();
1126
-
1127
- this.ctx.statusLine.invalidate();
1128
- this.ctx.updateEditorTopBorder();
1129
-
1130
- const sessionFile = this.ctx.session.sessionFile;
1131
- const shortPath = sessionFile ? sessionFile.split("/").pop() : "new session";
1132
- this.ctx.chatContainer.addChild(new Spacer(1));
1133
- this.ctx.chatContainer.addChild(
1134
- new Text(`${theme.fg("accent", `${theme.status.success} Session forked to ${shortPath}`)}`, 1, 1),
1135
- );
1136
- this.ctx.ui.requestRender();
1122
+ this.ctx.showUserMessageSelector();
1137
1123
  }
1138
1124
 
1139
1125
  async handleMoveCommand(targetPath: string): Promise<void> {
@@ -1186,23 +1172,63 @@ export class CommandController {
1186
1172
  }
1187
1173
  }
1188
1174
 
1189
- async handleRenameCommand(title: string): Promise<void> {
1175
+ async handleRenameCommand(title?: string): Promise<void> {
1190
1176
  try {
1177
+ if (!title?.trim()) {
1178
+ const generation = beginSessionTitleGeneration(this.ctx.sessionManager);
1179
+ const input = buildConversationTitleInput(this.ctx.session.messages);
1180
+ if (!input) {
1181
+ this.ctx.showError("Nothing to summarize yet — pass a title: /rename <title>");
1182
+ return;
1183
+ }
1184
+
1185
+ const generated = await generateSessionTitle(
1186
+ input,
1187
+ this.ctx.session.modelRegistry,
1188
+ this.ctx.settings,
1189
+ this.ctx.session.credentialSessionId,
1190
+ this.ctx.session.model,
1191
+ provider => this.ctx.session.agent.metadataForProvider(provider),
1192
+ );
1193
+ if (
1194
+ !isSessionTitleGenerationCurrent(this.ctx.sessionManager, generation) ||
1195
+ buildConversationTitleInput(this.ctx.session.messages) !== input
1196
+ )
1197
+ return;
1198
+ if (!generated) {
1199
+ this.ctx.showError("Could not generate a session title — pass one: /rename <title>");
1200
+ return;
1201
+ }
1202
+
1203
+ const stored = await this.ctx.sessionManager.setSessionName(generated, "user");
1204
+ if (!stored) {
1205
+ this.ctx.showError("Session name cannot be empty.");
1206
+ return;
1207
+ }
1208
+ this.#finishRename();
1209
+ return;
1210
+ }
1211
+
1212
+ invalidateSessionTitleGeneration(this.ctx.sessionManager);
1191
1213
  const stored = await this.ctx.sessionManager.setSessionName(title, "user");
1192
1214
  if (!stored) {
1193
1215
  this.ctx.showError("Session name cannot be empty.");
1194
1216
  return;
1195
1217
  }
1196
- const name = this.ctx.sessionManager.getSessionName()!;
1197
- setSessionTerminalTitle(name, this.ctx.sessionManager.getCwd());
1198
- this.ctx.statusLine.invalidate();
1199
- this.ctx.updateEditorBorderColor();
1200
- this.ctx.showStatus(`Session renamed to "${name}".`);
1218
+ this.#finishRename();
1201
1219
  } catch (err) {
1202
1220
  this.ctx.showError(`Rename failed: ${err instanceof Error ? err.message : String(err)}`);
1203
1221
  }
1204
1222
  }
1205
1223
 
1224
+ #finishRename(): void {
1225
+ const name = this.ctx.sessionManager.getSessionName()!;
1226
+ setSessionTerminalTitle(name, this.ctx.sessionManager.getCwd());
1227
+ this.ctx.statusLine.invalidate();
1228
+ this.ctx.updateEditorBorderColor();
1229
+ this.ctx.showStatus(`Session renamed to "${name}".`);
1230
+ }
1231
+
1206
1232
  async handleBashCommand(command: string, excludeFromContext = false): Promise<void> {
1207
1233
  const isDeferred = this.ctx.session.isStreaming;
1208
1234
  const component = new BashExecutionComponent(command, this.ctx.ui, excludeFromContext);