cli-jaw 2.17.62 → 2.17.63

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 (360) hide show
  1. package/README.ja.md +17 -17
  2. package/README.ko.md +17 -17
  3. package/README.md +17 -17
  4. package/README.zh-CN.md +17 -17
  5. package/dist/bin/commands/tui/input-handler.js +19 -0
  6. package/dist/bin/commands/tui/input-handler.js.map +1 -1
  7. package/dist/bin/commands/tui/overlays.js +8 -4
  8. package/dist/bin/commands/tui/overlays.js.map +1 -1
  9. package/dist/bin/star-prompt.js +1 -1
  10. package/dist/bin/star-prompt.js.map +1 -1
  11. package/dist/server.js +14 -1
  12. package/dist/server.js.map +1 -1
  13. package/dist/src/agent/claude-runtime-pool.js +2 -1
  14. package/dist/src/agent/claude-runtime-pool.js.map +1 -1
  15. package/dist/src/agent/claude-runtime-run.js +28 -7
  16. package/dist/src/agent/claude-runtime-run.js.map +1 -1
  17. package/dist/src/agent/lifecycle-handler.js +34 -6
  18. package/dist/src/agent/lifecycle-handler.js.map +1 -1
  19. package/dist/src/agent/runtime/claude-sdk-events.js +136 -9
  20. package/dist/src/agent/runtime/claude-sdk-events.js.map +1 -1
  21. package/dist/src/agent/runtime/claude-sdk-history-loader.js +24 -0
  22. package/dist/src/agent/runtime/claude-sdk-history-loader.js.map +1 -0
  23. package/dist/src/agent/runtime/claude-sdk-hooks.js +10 -4
  24. package/dist/src/agent/runtime/claude-sdk-hooks.js.map +1 -1
  25. package/dist/src/agent/runtime/claude-sdk-messages.js +25 -2
  26. package/dist/src/agent/runtime/claude-sdk-messages.js.map +1 -1
  27. package/dist/src/agent/runtime/claude-sdk-options.js +36 -3
  28. package/dist/src/agent/runtime/claude-sdk-options.js.map +1 -1
  29. package/dist/src/agent/runtime/claude-sdk-permissions.js +32 -9
  30. package/dist/src/agent/runtime/claude-sdk-permissions.js.map +1 -1
  31. package/dist/src/agent/runtime/claude-sdk-session.js +165 -17
  32. package/dist/src/agent/runtime/claude-sdk-session.js.map +1 -1
  33. package/dist/src/agent/runtime/claude-sdk-version.js +8 -0
  34. package/dist/src/agent/runtime/claude-sdk-version.js.map +1 -0
  35. package/dist/src/agent/spawn/backend-codex-app.js +619 -0
  36. package/dist/src/agent/spawn/backend-codex-app.js.map +1 -0
  37. package/dist/src/agent/spawn/backend-context.js +2 -0
  38. package/dist/src/agent/spawn/backend-context.js.map +1 -0
  39. package/dist/src/agent/spawn/backend-copilot.js +385 -0
  40. package/dist/src/agent/spawn/backend-copilot.js.map +1 -0
  41. package/dist/src/agent/spawn/backend-native-acp.js +379 -0
  42. package/dist/src/agent/spawn/backend-native-acp.js.map +1 -0
  43. package/dist/src/agent/spawn/backend-pi.js +565 -0
  44. package/dist/src/agent/spawn/backend-pi.js.map +1 -0
  45. package/dist/src/agent/spawn/types.js +2 -0
  46. package/dist/src/agent/spawn/types.js.map +1 -0
  47. package/dist/src/agent/spawn.js +44 -1858
  48. package/dist/src/agent/spawn.js.map +1 -1
  49. package/dist/src/cli/claude-default-model-boot.js +36 -0
  50. package/dist/src/cli/claude-default-model-boot.js.map +1 -0
  51. package/dist/src/cli/claude-model-discovery.js +13 -1
  52. package/dist/src/cli/claude-model-discovery.js.map +1 -1
  53. package/dist/src/cli/claude-models.js +7 -1
  54. package/dist/src/cli/claude-models.js.map +1 -1
  55. package/dist/src/cli/commands.js +5 -15
  56. package/dist/src/cli/commands.js.map +1 -1
  57. package/dist/src/cli/tui/composer.js +3 -3
  58. package/dist/src/cli/tui/composer.js.map +1 -1
  59. package/dist/src/cli/tui/skill-mention.js +15 -0
  60. package/dist/src/cli/tui/skill-mention.js.map +1 -0
  61. package/dist/src/code-mode/manager.js +174 -9
  62. package/dist/src/code-mode/manager.js.map +1 -1
  63. package/dist/src/code-mode/providers/catalog.js +5 -3
  64. package/dist/src/code-mode/providers/catalog.js.map +1 -1
  65. package/dist/src/code-mode/providers/claude-history.js +132 -0
  66. package/dist/src/code-mode/providers/claude-history.js.map +1 -0
  67. package/dist/src/code-mode/providers/claude.js +74 -7
  68. package/dist/src/code-mode/providers/claude.js.map +1 -1
  69. package/dist/src/code-mode/providers/codex-app.js +7 -1
  70. package/dist/src/code-mode/providers/codex-app.js.map +1 -1
  71. package/dist/src/code-mode/session.js +158 -11
  72. package/dist/src/code-mode/session.js.map +1 -1
  73. package/dist/src/code-mode/store.js +416 -28
  74. package/dist/src/code-mode/store.js.map +1 -1
  75. package/dist/src/command-contract/catalog.js +3 -22
  76. package/dist/src/command-contract/catalog.js.map +1 -1
  77. package/dist/src/core/codex-config.js +3 -62
  78. package/dist/src/core/codex-config.js.map +1 -1
  79. package/dist/src/core/config.js +116 -14
  80. package/dist/src/core/config.js.map +1 -1
  81. package/dist/src/core/runtime-settings.js +20 -10
  82. package/dist/src/core/runtime-settings.js.map +1 -1
  83. package/dist/src/core/settings-watch.js +1 -0
  84. package/dist/src/core/settings-watch.js.map +1 -1
  85. package/dist/src/core/skill-mentions.js +83 -0
  86. package/dist/src/core/skill-mentions.js.map +1 -0
  87. package/dist/src/manager/registry.js +5 -1
  88. package/dist/src/manager/registry.js.map +1 -1
  89. package/dist/src/manager/routes/desktop-status.js +1 -1
  90. package/dist/src/manager/routes/desktop-status.js.map +1 -1
  91. package/dist/src/orchestrator/pipeline.js +11 -1
  92. package/dist/src/orchestrator/pipeline.js.map +1 -1
  93. package/dist/src/prompt/builder.js +10 -4
  94. package/dist/src/prompt/builder.js.map +1 -1
  95. package/dist/src/prompt/templates/a1-system.md +1 -1
  96. package/dist/src/routes/code-native.js +51 -3
  97. package/dist/src/routes/code-native.js.map +1 -1
  98. package/dist/src/routes/settings.js +1 -4
  99. package/dist/src/routes/settings.js.map +1 -1
  100. package/dist/src/routes/skills.js +7 -0
  101. package/dist/src/routes/skills.js.map +1 -1
  102. package/dist/src/shared/skill-mention.js +61 -0
  103. package/dist/src/shared/skill-mention.js.map +1 -0
  104. package/dist/src/shared/tool-input.js +137 -0
  105. package/dist/src/shared/tool-input.js.map +1 -0
  106. package/package.json +7 -5
  107. package/public/css/activity.css +83 -20
  108. package/public/css/layout.css +2 -2
  109. package/public/css/sidebar.css +18 -0
  110. package/public/css/variables.css +23 -2
  111. package/public/dist/.vite/manifest.json +302 -334
  112. package/public/dist/assets/Activity-DPLlOJ46.js +1 -0
  113. package/public/dist/assets/AdvancedExport-BgOgGvc5.js +1 -0
  114. package/public/dist/assets/Agent-O_U8ny90.js +1 -0
  115. package/public/dist/assets/Browser-m6VynQXV.js +1 -0
  116. package/public/dist/assets/BrowserPanel-Bb-Bge6B.js +3 -0
  117. package/public/dist/assets/ChannelEnablementControl-BSuKQhTW.js +85 -0
  118. package/public/dist/assets/ChannelSetupEntry-DHLoZEzJ.js +1 -0
  119. package/public/dist/assets/ChannelsDiscord-B-G8vsYy.js +1 -0
  120. package/public/dist/assets/ChannelsSlack-oPuh922z.js +1 -0
  121. package/public/dist/assets/ChannelsTelegram-PmTv43KQ.js +1 -0
  122. package/public/dist/assets/CodeCanvas-CVbdp0S-.js +3 -0
  123. package/public/dist/assets/DashboardMeta-BqEiJrvr.js +1 -0
  124. package/public/dist/assets/DesignPanel-BOb-HMbl.js +2 -0
  125. package/public/dist/assets/{Developer-BdKPcw8Z.js → Developer-CbySB7_Z.js} +1 -1
  126. package/public/dist/assets/{DiffPanel-DtJuvU5y.js → DiffPanel-D9Cch5FG.js} +1 -1
  127. package/public/dist/assets/Display-C0Zdamcb.js +1 -0
  128. package/public/dist/assets/Display-DU4_76cS.js +1 -0
  129. package/public/dist/assets/DocPanel-DvtFw8AP.js +1 -0
  130. package/public/dist/assets/{Embedding-DvdTj1BF.js → Embedding-CCFMOB_2.js} +1 -1
  131. package/public/dist/assets/Employees-BanvF58d.js +1 -0
  132. package/public/dist/assets/FolderPanel-CJvwRwIV.js +3 -0
  133. package/public/dist/assets/Heartbeat-uyi_MpUd.js +1 -0
  134. package/public/dist/assets/InlineWarn-Cq4z28F3.js +1 -0
  135. package/public/dist/assets/MarkdownRenderer-Cc3DeHvm.js +1 -0
  136. package/public/dist/assets/{MarkdownRenderer-DhonsAPC.js → MarkdownRenderer-DzN66XVb.js} +1 -1
  137. package/public/dist/assets/Mcp-rjPoL_Zo.js +4 -0
  138. package/public/dist/assets/Memory-C9I33pIi.js +1 -0
  139. package/public/dist/assets/{MilkdownWysiwygEditor-D6JaLDE4.js → MilkdownWysiwygEditor-xObhviw7.js} +1 -1
  140. package/public/dist/assets/ModelProvider-fiUktMaO.js +1 -0
  141. package/public/dist/assets/Network-CuqFrOvR.js +1 -0
  142. package/public/dist/assets/NotesGraphWorkspace-DO9LtVGF.js +1 -0
  143. package/public/dist/assets/PanelLayoutProvider-DRZ4QkBC.js +1 -0
  144. package/public/dist/assets/Permissions-BltXiZnz.js +1 -0
  145. package/public/dist/assets/{Permissions-BwiMEoTV.js → Permissions-DcCbFMIq.js} +1 -1
  146. package/public/dist/assets/Profile-DUL7-RE_.js +1 -0
  147. package/public/dist/assets/Prompts-C9vY8ivQ.js +1 -0
  148. package/public/dist/assets/SpeechKeys-BJ8V7U5E.js +1 -0
  149. package/public/dist/assets/TelegramHub-DtyjLGFR.js +1 -0
  150. package/public/dist/assets/{TerminalPanel-Bu9i-P2f.js → TerminalPanel-c1xhiy53.js} +1 -1
  151. package/public/dist/assets/app-BjKzqXCv.css +1 -0
  152. package/public/dist/assets/app-CZwx0V6K.js +59 -0
  153. package/public/dist/assets/bgtask-badge-CnVkcp3c.js +1 -0
  154. package/public/dist/assets/{bgtask-badge-DfiHuzxT.js → bgtask-badge-Cp26S8Uq.js} +1 -1
  155. package/public/dist/assets/client-platform-WO8m6kPQ.js +1 -0
  156. package/public/dist/assets/{employees-BQaJNp-u.js → employees-CsvI0_5V.js} +1 -1
  157. package/public/dist/assets/error-normalize-clOi-AAA.js +1 -0
  158. package/public/dist/assets/fields-CicOWY-X.js +1 -0
  159. package/public/dist/assets/iframe-renderer-DWx9v2Iz.js +1 -0
  160. package/public/dist/assets/iframe-renderer-DmHYOI1A.js +142 -0
  161. package/public/dist/assets/{react-C_pPTFsM.js → jsx-runtime-CkEUZM6i.js} +1 -1
  162. package/public/dist/assets/manager-CCXm5Yfe.js +5 -0
  163. package/public/dist/assets/manager-D9rhKFKc.css +1 -0
  164. package/public/dist/assets/manager-shortcuts-Cqmr0mEq.js +1 -0
  165. package/public/dist/assets/{memory-EG1B51up.js → memory-C6qtAMdJ.js} +1 -1
  166. package/public/dist/assets/memory-PPofQPeB.js +1 -0
  167. package/public/dist/assets/message-history-DcjBuubA.js +4 -0
  168. package/public/dist/assets/message-history-bw4qF01Z.js +1 -0
  169. package/public/dist/assets/page-shell-DfinU3mb.js +1 -0
  170. package/public/dist/assets/react-dom-URBLzV3w.js +1 -0
  171. package/public/dist/assets/{render-Dp07lZAJ.js → render-DfYro-fG.js} +1 -1
  172. package/public/dist/assets/{settings-jlI4X-zt.js → settings-CvsLWlJn.js} +38 -30
  173. package/public/dist/assets/settings-DMiQe469.js +1 -0
  174. package/public/dist/assets/settings-DgAlohx9.js +1 -0
  175. package/public/dist/assets/settings-core-BH7J_Rbm.js +1 -0
  176. package/public/dist/assets/{settings-core-B5Uf_lWY.js → settings-core-pnpw-eZv.js} +1 -1
  177. package/public/dist/assets/{settings-embedding-BfWzWKMM.js → settings-embedding-BRW0WZvh.js} +3 -3
  178. package/public/dist/assets/{settings-embedding-DhEdT86W.css → settings-embedding-CUQ8Is3y.css} +1 -1
  179. package/public/dist/assets/settings-types-CYKy0xz4.js +1 -0
  180. package/public/dist/assets/shared-D1begqvi.js +1 -0
  181. package/public/dist/assets/{sidebar-BUemG2ZN.js → sidebar-C_80w4iY.js} +3 -3
  182. package/public/dist/assets/{skills-CKUcEN9m.js → skills-D_2adqge.js} +1 -1
  183. package/public/dist/assets/skills-DeNVAVgt.js +1 -0
  184. package/public/dist/assets/{slash-commands-D34GgHFX.js → slash-commands-B_Hq5RkC.js} +2 -2
  185. package/public/dist/assets/slash-commands-Cj5qoRWc.js +1 -0
  186. package/public/dist/assets/tool-input-DI7uu8Ah.js +2 -0
  187. package/public/dist/assets/{trace-drawer-DBmK-5F_.js → trace-drawer-B6XuPodh.js} +1 -1
  188. package/public/dist/assets/ui-BbR_fFiK.js +1 -0
  189. package/public/dist/assets/{ui-vm2iuLcS.js → ui-D8xzm7DN.js} +1 -1
  190. package/public/dist/index.html +11 -3
  191. package/public/dist/manager/index.html +3 -3
  192. package/public/dist/settings/index.html +2 -2
  193. package/public/index.html +9 -1
  194. package/public/js/features/activity-history.ts +50 -21
  195. package/public/js/features/activity-rows.ts +158 -10
  196. package/public/js/features/activity-view.ts +5 -5
  197. package/public/js/features/chat.ts +2 -0
  198. package/public/js/features/employees.ts +1 -0
  199. package/public/js/features/resize-guard.ts +39 -0
  200. package/public/js/features/settings-cli-status.ts +49 -17
  201. package/public/js/features/settings-types.ts +39 -1
  202. package/public/js/features/skill-mentions.ts +143 -0
  203. package/public/js/main.ts +15 -0
  204. package/public/js/virtual-scroll.ts +67 -1
  205. package/public/locales/en.json +2 -0
  206. package/public/locales/ja.json +2 -0
  207. package/public/locales/ko.json +2 -0
  208. package/public/locales/zh.json +2 -0
  209. package/public/manager/src/AppChrome.tsx +2 -0
  210. package/public/manager/src/InstancePreview.tsx +1 -1
  211. package/public/manager/src/SidebarRailRouter.tsx +3 -1
  212. package/public/manager/src/browser-panel/BrowserPanel.tsx +47 -12
  213. package/public/manager/src/browser-panel/browser-address-state.ts +11 -0
  214. package/public/manager/src/client-platform.ts +18 -0
  215. package/public/manager/src/code/CodeCanvas.tsx +4 -2
  216. package/public/manager/src/code/CodeComposer.tsx +22 -10
  217. package/public/manager/src/code/CodeDraftEmptyState.tsx +64 -0
  218. package/public/manager/src/code/CodeSessionList.tsx +154 -40
  219. package/public/manager/src/code/CodeTranscript.tsx +104 -16
  220. package/public/manager/src/code/CodeWorkbench.tsx +21 -11
  221. package/public/manager/src/code/CodeWorkspaceHeader.tsx +5 -10
  222. package/public/manager/src/code/ComposerFooter.tsx +9 -1
  223. package/public/manager/src/code/code-composer.css +1 -1
  224. package/public/manager/src/code/code-controller-draft-storage.ts +16 -8
  225. package/public/manager/src/code/code-controller-drafts.ts +33 -6
  226. package/public/manager/src/code/code-controller-runtime.ts +189 -17
  227. package/public/manager/src/code/code-controller-types.ts +19 -1
  228. package/public/manager/src/code/code-recent-workspaces.ts +25 -0
  229. package/public/manager/src/code/code-session-client.ts +19 -1
  230. package/public/manager/src/code/code-session-sidebar.css +38 -0
  231. package/public/manager/src/code/code-session-state.ts +2 -0
  232. package/public/manager/src/code/code-types.ts +47 -0
  233. package/public/manager/src/code/code-workbench.css +36 -1
  234. package/public/manager/src/code/code.css +171 -7
  235. package/public/manager/src/code/session-order.ts +80 -0
  236. package/public/manager/src/code/tool-summary.ts +45 -11
  237. package/public/manager/src/folder-panel/folder-shortcuts.ts +5 -9
  238. package/public/manager/src/hooks/useCommandPalette.ts +3 -7
  239. package/public/manager/src/main.tsx +1 -0
  240. package/public/manager/src/manager-components.css +25 -23
  241. package/public/manager/src/manager-dashboard-reminders.css +2 -2
  242. package/public/manager/src/manager-layout.css +1 -1
  243. package/public/manager/src/manager-shortcut-runner.ts +9 -2
  244. package/public/manager/src/manager-shortcuts.ts +83 -21
  245. package/public/manager/src/panels/desktop-bridge.ts +4 -1
  246. package/public/manager/src/panels/panels.css +9 -0
  247. package/public/manager/src/preview.ts +19 -4
  248. package/public/manager/src/settings/pages/AdvancedExport.tsx +28 -30
  249. package/public/manager/src/settings/pages/Browser.tsx +45 -47
  250. package/public/manager/src/settings/pages/ChannelsDiscord.tsx +51 -26
  251. package/public/manager/src/settings/pages/ChannelsSlack.tsx +59 -17
  252. package/public/manager/src/settings/pages/ChannelsTelegram.tsx +51 -26
  253. package/public/manager/src/settings/pages/Display.tsx +2 -1
  254. package/public/manager/src/settings/pages/Employees.tsx +3 -3
  255. package/public/manager/src/settings/pages/Mcp.tsx +220 -200
  256. package/public/manager/src/settings/pages/Memory.tsx +35 -41
  257. package/public/manager/src/settings/pages/ModelProvider.tsx +23 -98
  258. package/public/manager/src/settings/pages/Network.tsx +5 -4
  259. package/public/manager/src/settings/pages/Permissions.tsx +38 -16
  260. package/public/manager/src/settings/pages/Prompts.tsx +12 -9
  261. package/public/manager/src/settings/pages/SpeechKeys.tsx +132 -139
  262. package/public/manager/src/settings/pages/TelegramHub.tsx +67 -20
  263. package/public/manager/src/settings/pages/components/AvatarCard.tsx +9 -8
  264. package/public/manager/src/settings/pages/components/ChannelSetupEntry.tsx +2 -2
  265. package/public/manager/src/settings/pages/components/EmployeeRow.tsx +6 -12
  266. package/public/manager/src/settings/pages/components/HealthBadge.tsx +11 -6
  267. package/public/manager/src/settings/pages/components/HeartbeatJobRow.tsx +35 -16
  268. package/public/manager/src/settings/pages/components/HeartbeatJobsSection.tsx +11 -11
  269. package/public/manager/src/settings/pages/components/HeartbeatMarkdownSection.tsx +7 -7
  270. package/public/manager/src/settings/pages/components/InlineWarn.tsx +4 -9
  271. package/public/manager/src/settings/pages/components/McpServerCard.tsx +39 -27
  272. package/public/manager/src/settings/pages/components/MemoryRow.tsx +21 -16
  273. package/public/manager/src/settings/pages/components/PerCliRow.tsx +122 -121
  274. package/public/manager/src/settings/pages/components/PiProfileDialog.tsx +9 -6
  275. package/public/manager/src/settings/pages/components/SlackSetup.tsx +8 -8
  276. package/public/manager/src/settings/pages/components/TransportStatusChips.tsx +14 -10
  277. package/public/manager/src/settings/pages/components/agent/AgentEmployeesSection.tsx +26 -26
  278. package/public/manager/src/settings/pages/components/agent/PermissionQuickSection.tsx +5 -5
  279. package/public/manager/src/settings/pages/components/agent/RuntimeEmployeeRow.tsx +14 -11
  280. package/public/manager/src/settings/pages/components/agent/RuntimeHeader.tsx +1 -3
  281. package/public/manager/src/settings/pages/components/agent/agent-meta.ts +0 -2
  282. package/public/manager/src/settings/pages/components/runtime-transport-field.tsx +2 -2
  283. package/public/manager/src/settings/pages/manager/Activity.tsx +6 -2
  284. package/public/manager/src/settings/pages/manager/Display.tsx +3 -8
  285. package/public/manager/src/settings/pages/manager/shared.tsx +44 -33
  286. package/public/manager/src/settings/pages/page-shell.tsx +66 -0
  287. package/public/manager/src/settings-agent.css +185 -36
  288. package/public/manager/src/settings-standalone.tsx +1 -0
  289. package/public/manager/src/settings-surface.css +96 -0
  290. package/public/manager/src/types.ts +1 -0
  291. package/scripts/ci/install-ripgrep.sh +35 -0
  292. package/scripts/collect-fresh-install-evidence.sh +2 -2
  293. package/scripts/install-risk-gate.mjs +2 -3
  294. package/scripts/install-wsl.sh +1 -1
  295. package/scripts/install.ps1 +1 -1
  296. package/scripts/install.sh +1 -1
  297. package/scripts/release-1.6.0.sh +1 -1
  298. package/scripts/release-gates.mjs +48 -13
  299. package/scripts/release-preview.sh +2 -2
  300. package/scripts/restack-pr.sh +88 -0
  301. package/scripts/verify-electron-update-metadata.mjs +1 -1
  302. package/scripts/windows-support-contract.json +1 -1
  303. package/public/dist/assets/Activity-BUrp73Re.js +0 -1
  304. package/public/dist/assets/AdvancedExport-B7h_ZvBu.js +0 -1
  305. package/public/dist/assets/Agent-DnbZJP7b.js +0 -1
  306. package/public/dist/assets/Browser-CiBoK5Qx.js +0 -1
  307. package/public/dist/assets/BrowserPanel-SZAUc9T2.js +0 -3
  308. package/public/dist/assets/ChannelEnablementControl-Dl0uYx38.js +0 -85
  309. package/public/dist/assets/ChannelSetupEntry-DxtOrN01.js +0 -1
  310. package/public/dist/assets/ChannelsDiscord-BEGGwd_3.js +0 -1
  311. package/public/dist/assets/ChannelsSlack-BryC2Ahr.js +0 -1
  312. package/public/dist/assets/ChannelsTelegram-CGSpaImJ.js +0 -1
  313. package/public/dist/assets/CodeCanvas-DpkFi5Au.js +0 -2
  314. package/public/dist/assets/DashboardMeta-Dh-tasbY.js +0 -1
  315. package/public/dist/assets/DesignPanel-NLNK9wCo.js +0 -2
  316. package/public/dist/assets/Display-BHGFX3O0.js +0 -1
  317. package/public/dist/assets/Display-BW-_pKRf.js +0 -1
  318. package/public/dist/assets/DocPanel-BM41cYpS.js +0 -1
  319. package/public/dist/assets/Employees-BuFw6-fT.js +0 -1
  320. package/public/dist/assets/FolderPanel-C-eQT3Yq.js +0 -3
  321. package/public/dist/assets/Heartbeat-CIVtKWNc.js +0 -1
  322. package/public/dist/assets/InlineWarn-DIMbcbzk.js +0 -1
  323. package/public/dist/assets/MarkdownRenderer-DyVx6DxM.js +0 -1
  324. package/public/dist/assets/Mcp-uKjor7vh.js +0 -4
  325. package/public/dist/assets/Memory-BSDIXT0B.js +0 -1
  326. package/public/dist/assets/ModelProvider-DMh2tXUx.js +0 -1
  327. package/public/dist/assets/Network-4Pa6JkhK.js +0 -1
  328. package/public/dist/assets/NotesGraphWorkspace-CgdC-fM6.js +0 -1
  329. package/public/dist/assets/PanelLayoutProvider-mn8sI0h-.js +0 -1
  330. package/public/dist/assets/Permissions-CodIW-Yd.js +0 -1
  331. package/public/dist/assets/Profile-DPqrYA44.js +0 -1
  332. package/public/dist/assets/Prompts-CvAx7QqR.js +0 -1
  333. package/public/dist/assets/SpeechKeys-CVBCqoBn.js +0 -1
  334. package/public/dist/assets/TelegramHub-4zKwpk0P.js +0 -1
  335. package/public/dist/assets/app-ByMfeXp-.js +0 -55
  336. package/public/dist/assets/app-DAJWqs33.css +0 -1
  337. package/public/dist/assets/bgtask-badge-Ch88fnhf.js +0 -1
  338. package/public/dist/assets/error-normalize-CL69Bl7z.js +0 -1
  339. package/public/dist/assets/fields-mlGpofqQ.js +0 -1
  340. package/public/dist/assets/iframe-renderer-Bj1JolO8.js +0 -142
  341. package/public/dist/assets/iframe-renderer-DWnvUSrx.js +0 -1
  342. package/public/dist/assets/jsx-runtime-NZYk81nU.js +0 -1
  343. package/public/dist/assets/manager-Bi_xw7TW.js +0 -5
  344. package/public/dist/assets/manager-BnQHAKk6.css +0 -1
  345. package/public/dist/assets/manager-shortcuts-BDOzd8zm.js +0 -1
  346. package/public/dist/assets/memory-Bk_cb3sw.js +0 -1
  347. package/public/dist/assets/message-history-D2vniu04.js +0 -4
  348. package/public/dist/assets/message-history-GAtF_wbZ.js +0 -1
  349. package/public/dist/assets/page-shell-BeGD0rRQ.js +0 -1
  350. package/public/dist/assets/react-dom-DyUOYpVL.js +0 -1
  351. package/public/dist/assets/settings-W4KG9ivu.js +0 -1
  352. package/public/dist/assets/settings-WVGhtneN.js +0 -1
  353. package/public/dist/assets/settings-client-CmwRsVd1.js +0 -1
  354. package/public/dist/assets/settings-core-CgKGnMA5.js +0 -1
  355. package/public/dist/assets/settings-types-D3cd6SWy.js +0 -1
  356. package/public/dist/assets/shared-CiNP20HB.js +0 -1
  357. package/public/dist/assets/skills-Co7Mr-qP.js +0 -1
  358. package/public/dist/assets/slash-commands-BEr-vmIr.js +0 -1
  359. package/public/dist/assets/ui-TUyJsMUf.js +0 -1
  360. package/public/manager/src/settings/pages/components/EngineSubsection.tsx +0 -25
@@ -1,13 +1,15 @@
1
- import { lazy, Suspense, useCallback, useMemo, useRef, useState, type KeyboardEvent } from 'react';
1
+ import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState, type KeyboardEvent } from 'react';
2
2
  import type { CodeItem, CodeItemKind, CodeProviderId } from '../../../../src/code-mode/wire';
3
3
  import { useCodeTranscriptVirtualRows } from './useCodeTranscriptVirtualRows';
4
4
  import { useCodeTranscriptScroll } from './use-code-transcript-scroll';
5
5
  import { useThrottledMarkdown } from './use-throttled-markdown';
6
6
  import { CODE_RUNTIME_LABELS, codeItemStatus } from './code-types';
7
- import { noteworthyStatus, toolSummary } from './tool-summary';
7
+ import { noteworthyStatus, toolInputDescription, toolInputDisplay, toolSummary } from './tool-summary';
8
+ import { copyText } from '../clipboard/copy-text';
8
9
  import { PENDING_USER_ITEM_ID } from './pending-user-item';
9
10
 
10
11
  const MarkdownRenderer = lazy(() => import('../notes/rendering/MarkdownRenderer').then(m => ({ default: m.MarkdownRenderer })));
12
+ export const CODE_ROLLBACK_CONFIRM = 'Later turns are removed from this conversation. Workspace files are not changed. No prompt is sent.';
11
13
 
12
14
  /**
13
15
  * Turn boundaries are bookkeeping, not conversation. A one-line answer framed by
@@ -24,15 +26,22 @@ const HIDDEN_KINDS: ReadonlySet<CodeItemKind> = new Set<CodeItemKind>(['turn_sta
24
26
  /**
25
27
  * What a collapsible row measures before it has been measured. Collapsed is one
26
28
  * line and exact. Expanded is a guess and deliberately only that: the panes are
27
- * capped at 200px each by `.code-tool-output` / `.code-tool-args`, so a call
28
- * with both is far taller, and the real number arrives from the ResizeObserver
29
- * a frame later. This only has to be closer than one line.
29
+ * capped at min(18rem,50dvh) each by `.code-tool-output` / `.code-tool-args`,
30
+ * so a call with both is far taller, and the real number arrives from the
31
+ * ResizeObserver a frame later. This only has to be closer than one line.
30
32
  */
31
33
  const COLLAPSED_ROW_PX = 44;
32
- const EXPANDED_ROW_PX = 320;
34
+ const EXPANDED_ROW_PX = 480;
33
35
  /** Same bound the scroll anchors use, for the same reason. */
34
36
  const MAX_OPEN_ROWS = 64;
35
37
 
38
+ // Past ~15 wrapped lines the capped scroll height already engages; only then
39
+ // is a Show-all toggle worth showing (jsdom cannot measure overflow either, so
40
+ // this estimates by length the same way the activity rows do).
41
+ function isTallToolText(value: string): boolean {
42
+ return value.length > 1400 || value.split('\n').length > 14;
43
+ }
44
+
36
45
  /**
37
46
  * A failed call opens itself, because the reader has to see why it failed. That
38
47
  * is a default, not a lock: once the reader has said what they want for this
@@ -52,11 +61,13 @@ function ItemMarkdown({ item, identity, onOpenLocalFile }: { item: CodeItem; ide
52
61
  </Suspense>;
53
62
  }
54
63
 
55
- export function CodeTranscriptItem({ item, provider, sessionKey, workingDir = '', onOpenLocalFile, expanded, onExpandedChange }: {
64
+ export function CodeTranscriptItem({ item, provider, sessionKey, workingDir = '', onOpenLocalFile, expanded, onExpandedChange, onRollback }: {
56
65
  item: CodeItem; provider: CodeProviderId; sessionKey: string; workingDir?: string;
57
66
  onOpenLocalFile?: ((path: string) => void) | undefined;
58
67
  expanded?: boolean;
59
68
  onExpandedChange?: ((itemId: string, open: boolean) => void) | undefined;
69
+ /** Present only on a row the conversation may be rolled back to; asks before calling. */
70
+ onRollback?: ((itemId: string) => void) | undefined;
60
71
  }) {
61
72
  const tool = item.kind === 'tool_call' || item.kind === 'file_change';
62
73
  const reasoning = item.kind === 'reasoning';
@@ -78,6 +89,53 @@ export function CodeTranscriptItem({ item, provider, sessionKey, workingDir = ''
78
89
  // the duplicate back. Failure and cancellation still get a word, because
79
90
  // those change what to do next.
80
91
  const toolNote = running ? null : note;
92
+ const description = tool ? toolInputDescription(item.tool?.input) : '';
93
+ const inputDisplay = tool ? toolInputDisplay(item.tool?.input) : null;
94
+ const toolBlocks: Array<{ key: string; label: string; content: string; className: string; copyable: boolean }> = [];
95
+ if (tool) {
96
+ if (inputDisplay) toolBlocks.push({ key: 'input', label: 'Input', content: inputDisplay.content, className: 'code-tool-args', copyable: true });
97
+ if (inputDisplay?.parameters) toolBlocks.push({ key: 'params', label: 'Parameters', content: inputDisplay.parameters, className: 'code-tool-json', copyable: false });
98
+ if (inputDisplay?.description) toolBlocks.push({ key: 'description', label: 'Description', content: inputDisplay.description, className: 'code-tool-text', copyable: false });
99
+ if (item.tool?.output !== undefined) toolBlocks.push({ key: 'output', label: 'Output', content: item.tool.output, className: 'code-tool-output', copyable: false });
100
+ }
101
+ const toolBodyTall = toolBlocks.some(block => isTallToolText(block.content))
102
+ || (item.tool?.detail !== undefined && isTallToolText(item.tool.detail))
103
+ || (item.text !== undefined && isTallToolText(item.text));
104
+ const [showAll, setShowAll] = useState(false);
105
+ const bodyRef = useRef<HTMLDivElement | null>(null);
106
+ const [measuredTall, setMeasuredTall] = useState<boolean | null>(null);
107
+ // A measurement describes the body it was taken from, and the estimate is
108
+ // what moves when that body changes, so a number taken while the content was
109
+ // taller is dropped instead of deciding for the shorter content in its place.
110
+ useEffect(() => { setMeasuredTall(null); }, [toolBodyTall]);
111
+ // The toggle measures real overflow once the body lays out (browser); where
112
+ // layout cannot be measured (jsdom reports 0 everywhere) the character
113
+ // estimate stands in, the same rule the activity rows use. Every capped pane
114
+ // is measured, not only the <pre> ones: the call's own detail is a paragraph
115
+ // under the same cap. While expanded the cap is off, so measuring is skipped
116
+ // — the button must stay to toggle back — and closing the body resets the
117
+ // measure for the next open.
118
+ useEffect(() => {
119
+ if (!open || showAll) return;
120
+ const panes = bodyRef.current?.querySelectorAll('pre, .code-tool-body > .code-tool-text');
121
+ if (!panes || panes.length === 0) { setMeasuredTall(null); return; }
122
+ const list = [...panes];
123
+ const measured = list.some(pane => pane.scrollHeight > pane.clientHeight + 2);
124
+ setMeasuredTall(measured || (list.every(pane => pane.scrollHeight === 0) && toolBodyTall));
125
+ }, [open, showAll, toolBodyTall]);
126
+ const toolBodyOverflowing = measuredTall ?? toolBodyTall;
127
+ // Content that no longer overflows has nothing left to disclose: leaving the
128
+ // disclosure set would hold a "Show less" button, and an uncapped body, over
129
+ // a call that already fits.
130
+ useEffect(() => { if (!toolBodyOverflowing) setShowAll(false); }, [toolBodyOverflowing]);
131
+ const [copied, setCopied] = useState(false);
132
+ const copiedTimerRef = useRef<number | null>(null);
133
+ useEffect(() => () => { if (copiedTimerRef.current !== null) window.clearTimeout(copiedTimerRef.current); }, []);
134
+ const markCopied = useCallback(() => {
135
+ setCopied(true);
136
+ if (copiedTimerRef.current !== null) window.clearTimeout(copiedTimerRef.current);
137
+ copiedTimerRef.current = window.setTimeout(() => { copiedTimerRef.current = null; setCopied(false); }, 1200);
138
+ }, []);
81
139
  const label = user ? 'You' : assistant ? CODE_RUNTIME_LABELS[provider] : reasoning ? 'Reasoning'
82
140
  : item.kind === 'turn_started' ? 'Turn started' : item.kind === 'session_runtime' ? 'Runtime'
83
141
  : item.kind === 'permission_request' ? 'Permission record' : item.kind === 'notice' ? 'Notice' : status;
@@ -86,36 +144,60 @@ export function CodeTranscriptItem({ item, provider, sessionKey, workingDir = ''
86
144
  {tool ? <details className={`code-tool-card code-tool-${item.status}`} open={open}
87
145
  onToggle={event => onExpandedChange?.(item.itemId, event.currentTarget.open)}>
88
146
  <summary className="code-tool-summary"><span className="code-tool-chevron" aria-hidden="true">›</span>
89
- <span className={`code-tool-name${running ? ' code-tool-name-running' : ''}`}>{toolSummary(item, workingDir)}</span>
147
+ <span className="code-tool-summary-text">
148
+ <span className={`code-tool-name${running ? ' code-tool-name-running' : ''}`}>{toolSummary(item, workingDir)}</span>
149
+ {description !== '' && <span className="code-tool-desc">{description}</span>}
150
+ </span>
90
151
  {toolNote && <span className="code-tool-status">{toolNote}</span>}</summary>
91
152
  {/* Built only while open: a collapsed call's output can be megabytes,
92
153
  and constructing it costs the same whether or not it is painted. */}
93
- {open && <>
154
+ {open && <div ref={bodyRef} className="code-tool-body" data-expanded={showAll}>
94
155
  {item.tool?.detail !== undefined && <p className="code-tool-text">{item.tool.detail}</p>}
95
- {item.tool?.input !== undefined && <section className="code-tool-section"><span className="code-tool-section-label">Input</span><pre className="code-tool-args">{item.tool.input}</pre></section>}
96
- {item.tool?.output !== undefined && <section className="code-tool-section"><span className="code-tool-section-label">Output</span><pre className="code-tool-output">{item.tool.output}</pre></section>}
156
+ {toolBlocks.map(block => <section key={block.key} className="code-tool-section">
157
+ <div className="code-tool-section-head">
158
+ <span className="code-tool-section-label">{block.label}</span>
159
+ {block.copyable && <button type="button" className={`code-tool-copy${copied ? ' copied' : ''}`}
160
+ aria-label={`Copy ${block.label.toLowerCase()}`}
161
+ onClick={() => void copyText(block.content).then(result => { if (result.ok) markCopied(); })}>
162
+ {copied ? 'Copied' : 'Copy'}
163
+ </button>}
164
+ </div>
165
+ <pre className={block.className}>{block.content}</pre>
166
+ </section>)}
97
167
  {item.text !== undefined && <pre className="code-tool-text">{item.text}</pre>}
98
- </>}
168
+ {running && item.tool?.output === undefined && item.tool?.detail === undefined
169
+ && <p className="code-tool-waiting">Waiting for output…</p>}
170
+ {toolBodyOverflowing && <button type="button" className="code-tool-toggle" aria-expanded={showAll}
171
+ onClick={() => setShowAll(value => !value)}>{showAll ? 'Show less' : 'Show all'}</button>}
172
+ </div>}
99
173
  </details> : reasoning ? <details className="code-thinking" open={open}
100
174
  onToggle={event => onExpandedChange?.(item.itemId, event.currentTarget.open)}>
101
175
  <summary className={`code-thinking-summary${item.status === 'running' ? ' code-tool-name-running' : ''}`}>{item.status === 'running' ? 'Thinking…' : 'Reasoning'}</summary>
102
176
  {open && <div className="code-thinking-text">{item.text}</div>}
103
177
  </details> : <>
104
178
  <span className="code-message-role">{label}{assistant && item.phase === 'commentary' ? ' · Commentary' : ''}
105
- {note && ` · ${unsent ? 'Sending' : note}`}</span>
179
+ {note && ` · ${unsent ? 'Sending' : note}`}
180
+ {/* A follow-up no native result consumed before the turn ended. */}
181
+ {user && !unsent && item.phase === 'unknown' && ' · Delivery not confirmed'}</span>
106
182
  <div className="code-message-text">{assistant ? <ItemMarkdown item={item} identity={`${sessionKey}:${item.itemId}`} onOpenLocalFile={onOpenLocalFile} />
107
183
  : <span className="code-plain-text">{item.text ?? item.permission?.title ?? ''}</span>}</div>
108
184
  {item.permission?.detail && <p className="code-plain-text">{item.permission.detail}</p>}
185
+ {onRollback && user && !unsent && <button type="button" className="code-message-rollback"
186
+ onClick={() => { if (window.confirm(CODE_ROLLBACK_CONFIRM)) onRollback(item.itemId); }}>Roll back conversation to here</button>}
109
187
  </>}
110
188
  {(assistant || reasoning) && (item.status === 'cancelled' || item.status === 'error') && <span className="code-partial-label">Partial output · {status}</span>}
111
189
  {item.truncation && <p className="code-truncation" role="note">Output truncated: {item.truncation.storedChars.toLocaleString()} of {item.truncation.sourceChars.toLocaleString()} characters retained.</p>}
112
190
  </article>;
113
191
  }
114
192
 
115
- export function CodeTranscript({ items, provider, sessionKey, workingDir, loading, hasOlderHistory, loadOlderHistory, permissionCount, onOpenLocalFile }: {
116
- items: CodeItem[]; provider: CodeProviderId; sessionKey: string; workingDir: string; loading: boolean;
193
+ export function CodeTranscript({ items, provider, sessionKey, workingDir, loading, hasOlderHistory, loadOlderHistory, permissionCount, onOpenLocalFile, working = false,
194
+ rollbackRows, onRollback }: {
195
+ items: CodeItem[]; provider: CodeProviderId; sessionKey: string; workingDir: string; loading: boolean; working?: boolean;
117
196
  hasOlderHistory: boolean; loadOlderHistory(): Promise<void>; permissionCount: number;
118
197
  onOpenLocalFile?: ((path: string) => void) | undefined;
198
+ /** Opaque user row ids that may be rollback targets; the transcript never sees native ids. */
199
+ rollbackRows?: ReadonlySet<string> | undefined;
200
+ onRollback?: ((itemId: string) => void) | undefined;
119
201
  }) {
120
202
  const transcriptRef = useRef<HTMLDivElement>(null);
121
203
  const visible = useMemo(() => items.filter(item => !HIDDEN_KINDS.has(item.kind)), [items]);
@@ -209,10 +291,16 @@ export function CodeTranscript({ items, provider, sessionKey, workingDir, loadin
209
291
  <CodeTranscriptItem item={item} provider={provider} sessionKey={sessionKey}
210
292
  workingDir={workingDir} onOpenLocalFile={onOpenLocalFile}
211
293
  expanded={isRowOpen(openRows, sessionKey, item)}
212
- onExpandedChange={setExpanded} />
294
+ onExpandedChange={setExpanded}
295
+ onRollback={rollbackRows?.has(item.itemId) ? onRollback : undefined} />
213
296
  </div> : null;
214
297
  })}
215
298
  </div>}
299
+ {/* From Send until the turn ends. A running tool or reasoning row already
300
+ animates, so the line would only repeat it. */}
301
+ {working && visible[visible.length - 1]?.status !== 'running'
302
+ && <div className="code-transcript-working" role="status" aria-live="polite">
303
+ <span className="code-session-spinner" aria-hidden="true" />Working…</div>}
216
304
  </div>
217
305
  </>;
218
306
  }
@@ -2,12 +2,13 @@ import { useCallback, useRef, useState } from 'react';
2
2
  import type { CodeControllerModel } from './code-controller-types';
3
3
  import { CodeComposer } from './CodeComposer';
4
4
  import { ComposerFooter } from './ComposerFooter';
5
+ import { CodeDraftEmptyState } from './CodeDraftEmptyState';
5
6
  import { CodePermissionQueue } from './CodePermissionQueue';
6
7
  import { CodeTranscript } from './CodeTranscript';
7
8
  import { CodeToastHost } from './CodeToastHost';
8
9
  import { applyCodeNotice, dismissCodeToast, type CodeNotice, type CodeToast } from './code-toasts';
9
10
  import { CodeWorkspaceHeader } from './CodeWorkspaceHeader';
10
- import { codeCanResume } from './code-types';
11
+ import { codeCanResume, codeCanRollback, codeResendCopy, codeRollbackRows } from './code-types';
11
12
 
12
13
  type Props = { controller: CodeControllerModel; endpointKey: string; onOpenLocalFile?: ((path: string) => void) | undefined };
13
14
  export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: Props) {
@@ -26,15 +27,20 @@ export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: P
26
27
  const dismiss = useCallback((id: string) => setToasts(current => dismissCodeToast(current, id)), []);
27
28
  const archived = c.session?.archivedAt != null;
28
29
  const stopping = c.session?.status === 'stopping' || c.operation.kind === 'stopping';
29
- const busy = c.busy || stopping || c.session?.status === 'starting' || c.session?.status === 'streaming';
30
+ const busy = c.busy || c.working || stopping || c.session?.status === 'starting' || c.session?.status === 'streaming';
30
31
  const provider = c.catalog?.providers.find(p => p.id === c.selection.provider);
31
32
  const unknownSend = c.operation.kind === 'unknown-send';
32
- const canSend = !busy && !c.pending && !c.creationUnknown && !archived && c.operation.kind === 'idle' && !!provider?.available
33
+ // A streaming Claude turn takes one follow-up beside Stop; any other busy session offers Stop only.
34
+ const canSend = !c.steering && (c.followUp ? !c.followUpSent && !stopping && !c.pending && !archived && c.operation.kind === 'idle'
35
+ : !busy && !c.pending && !c.creationUnknown && !archived && c.operation.kind === 'idle' && !!provider?.available
33
36
  && !!c.selection.cwd.trim() && !!c.selection.model.trim()
34
- && (c.selectedId === null || (c.synced && c.session?.status === 'idle'));
37
+ // A suspended or recoverably failed session can be sent to: send attaches it first.
38
+ && (c.selectedId === null || (c.synced && (c.session?.status === 'idle' || (!!c.session && codeCanResume(c.session))))));
35
39
  const canStop = !!c.session?.turnId && c.session.capabilities.interrupt && busy && !stopping;
36
40
  const canResume = !!c.session && codeCanResume(c.session) && c.synced && !c.pending && !unknownSend;
37
41
  const error = c.operation.error || c.error || (actionError?.key === sessionKey ? actionError.message : null);
42
+ const rollbackRows = codeCanRollback(c.session, c.synced, c.operation.kind === 'idle' && !c.steering)
43
+ ? codeRollbackRows(c.items, c.session?.rollback?.sinceSequence) : undefined;
38
44
  const terminal = [...c.items].reverse().find(item => item.kind === 'turn_cancelled' || item.kind === 'turn_failed' || item.kind === 'turn_completed');
39
45
  const failedInput = terminal && terminal.kind !== 'turn_completed'
40
46
  ? c.items.find(item => item.kind === 'user_message' && item.turnId === terminal.turnId)?.text : undefined;
@@ -67,12 +73,12 @@ export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: P
67
73
  <button type="button" onClick={c.startAnotherSession}>Start another session</button>
68
74
  </section>}
69
75
  {unknownSend && <section className="code-recovery-strip"
70
- aria-label={c.resendRequired ? 'Send not started' : 'Unconfirmed send'}>
71
- <strong>{c.resendRequired ? 'Send did not start' : 'Send outcome not confirmed'}</strong>
76
+ aria-label={c.resendRequired ? (c.resendReason === 'rolled-back' ? 'Send rolled back' : 'Send not started') : 'Unconfirmed send'}>
77
+ <strong>{c.resendRequired ? codeResendCopy(c.resendReason ?? undefined).heading : 'Send outcome not confirmed'}</strong>
72
78
  {/* Once the server has spent the key, acceptance is no longer unknown,
73
79
  so this must stop offering the weaker, hedged explanation. */}
74
80
  <p>{c.resendRequired
75
- ? 'The original attempt ended on the server without running. Retry sends this as a new message.'
81
+ ? codeResendCopy(c.resendReason ?? undefined).detail
76
82
  : 'Retry uses the original request. It may submit it if the server has not already accepted it.'}</p>
77
83
  <pre className="code-retry-preview" aria-label="Original prompt">{c.retryText}</pre>
78
84
  <button type="button" disabled={!c.canRetrySameSend || retrying === sessionKey} onClick={() => void retry()}>
@@ -90,9 +96,12 @@ export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: P
90
96
  {/* Directly above the transcript, so a notice sits over the conversation
91
97
  rather than over the workspace header and its picker. */}
92
98
  <CodeToastHost toasts={toasts} onDismiss={dismiss} />
93
- <CodeTranscript items={c.items} provider={c.session?.provider ?? c.selection.provider} sessionKey={sessionKey}
94
- workingDir={c.session?.cwd ?? c.selection.cwd} loading={c.loading} hasOlderHistory={c.hasOlderHistory}
95
- loadOlderHistory={c.loadOlderHistory} permissionCount={c.permissions.length} onOpenLocalFile={onOpenLocalFile} />
99
+ {c.selectedId === null && c.items.length === 0
100
+ ? <CodeDraftEmptyState controller={c} />
101
+ : <CodeTranscript items={c.items} provider={c.session?.provider ?? c.selection.provider} sessionKey={sessionKey}
102
+ workingDir={c.session?.cwd ?? c.selection.cwd} loading={c.loading} hasOlderHistory={c.hasOlderHistory}
103
+ loadOlderHistory={c.loadOlderHistory} permissionCount={c.permissions.length} onOpenLocalFile={onOpenLocalFile}
104
+ working={c.working} rollbackRows={rollbackRows} onRollback={itemId => perform(() => c.rollbackSession(itemId))} />}
96
105
  <CodePermissionQueue permissions={c.permissions} operations={c.permissionOperations} session={c.session} synced={c.synced} onAnswer={c.answer} />
97
106
  <div className="code-composer-dock">
98
107
  {failedInput !== undefined && !busy && !archived && <div className="code-input-recovery">
@@ -102,7 +111,8 @@ export function CodeWorkbench({ controller: c, endpointKey, onOpenLocalFile }: P
102
111
  </div>}
103
112
  <div className="code-composer-surface" aria-label="Code composer controls">
104
113
  <CodeComposer key={`composer:${sessionKey}`} inputText={c.input} canSend={canSend} busy={busy} canStop={canStop} stopping={stopping}
105
- pending={c.pending} readOnly={archived} onInputChange={c.setInput} onSubmit={c.send} onStop={c.stop} />
114
+ pending={c.pending} followUp={c.followUp} followUpSent={c.followUpSent} steering={c.steering} readOnly={archived} autoFocus={c.selectedId === null} onInputChange={c.setInput} onSubmit={c.send} onStop={c.stop}
115
+ {...(c.operation.kind === 'rolling-back' ? { pendingLabel: 'Rolling back conversation…' } : {})} />
106
116
  <ComposerFooter key={`footer:${sessionKey}`} controller={c} onNotice={notify} />
107
117
  </div>
108
118
  </div>
@@ -1,18 +1,14 @@
1
- import { useState } from 'react';
2
1
  import type { CodeControllerModel } from './code-controller-types';
3
2
  import { CODE_RUNTIME_LABELS, CODE_SESSION_LABELS } from './code-types';
4
3
 
5
4
  export function CodeWorkspaceHeader({ controller: c }: { controller: CodeControllerModel }) {
6
- const [picking, setPicking] = useState(false);
7
- const [error, setError] = useState<string | null>(null);
8
5
  const cwd = c.session?.cwd ?? c.selection.cwd;
9
6
  const frozen = c.selectedId !== null || c.pending || c.creationUnknown || c.operation.kind !== 'idle';
10
- async function pick() {
7
+ // Shared with CodeDraftEmptyState's picker so only one folder dialog can be open.
8
+ const picking = c.workspacePicking;
9
+ function pick() {
11
10
  if (frozen || picking) return;
12
- setPicking(true); setError(null);
13
- try { await c.pickWorkspace(); }
14
- catch (err) { setError(err instanceof Error ? err.message : String(err)); }
15
- finally { setPicking(false); }
11
+ void c.pickWorkspace();
16
12
  }
17
13
  return <header className="code-workspace-header">
18
14
  <div className="code-session-header">
@@ -23,7 +19,7 @@ export function CodeWorkspaceHeader({ controller: c }: { controller: CodeControl
23
19
  <div className="code-workspace-primary">
24
20
  {frozen ? <span className="code-workspace-chip" title={cwd}>{cwd || 'Workspace not set'}</span>
25
21
  : <button type="button" className="code-workspace-picker" aria-label="Choose Code workspace" disabled={picking}
26
- title={cwd || 'Choose a folder'} onClick={() => void pick()}>
22
+ title={cwd || 'Choose a folder'} onClick={pick}>
27
23
  <span className="code-workspace-picker-label">{picking ? 'Choosing folder…' : cwd || 'Choose workspace'}</span>
28
24
  </button>}
29
25
  {c.gitInfo?.isRepo && <>
@@ -32,7 +28,6 @@ export function CodeWorkspaceHeader({ controller: c }: { controller: CodeControl
32
28
  {c.gitInfo.status.dirty ? `${c.gitInfo.status.changed} changed · ${c.gitInfo.status.untracked} untracked` : 'clean'}
33
29
  </span>}
34
30
  </>}
35
- {error && <span className="code-action-error" role="alert">{error}</span>}
36
31
  </div>
37
32
  </header>;
38
33
  }
@@ -121,6 +121,9 @@ export function ComposerFooter({ controller: c, onNotice }: {
121
121
  const selection = c.selection;
122
122
  const provider = c.catalog?.providers.find(entry => entry.id === selection.provider);
123
123
  const capabilities = c.session?.capabilities ?? provider?.capabilities;
124
+ // Claude switches permission modes live, so its menu follows today's catalog rather
125
+ // than the snapshot a session was created with; other runtimes keep their snapshot.
126
+ const permissionModes = (selection.provider === 'claude' ? provider?.capabilities : capabilities)?.permissionModes ?? [];
124
127
  const locked = c.selectedId !== null;
125
128
  const disabled = c.pending || c.busy || c.creationUnknown || c.operation.kind !== 'idle' || (locked && (!c.synced || c.session?.status !== 'idle' || c.session?.archivedAt !== null));
126
129
  const [saving, setSaving] = useState(false);
@@ -183,7 +186,7 @@ export function ComposerFooter({ controller: c, onNotice }: {
183
186
  }} />
184
187
  <CodeFooterMenu label="Permission" value={selection.permissionMode} displayValue={CODE_POLICY_LABELS[selection.permissionMode]}
185
188
  disabled={controlsDisabled || !capabilities?.permissions}
186
- options={(capabilities?.permissionModes ?? []).map(value => ({ value, label: CODE_POLICY_LABELS[value], detail: CODE_POLICY_DETAILS[value] }))}
189
+ options={permissionModes.map(value => ({ value, label: CODE_POLICY_LABELS[value], detail: CODE_POLICY_DETAILS[value] }))}
187
190
  onChange={value => void change({ permissionMode: value })} />
188
191
  <span className="code-composer-footer-spacer" aria-hidden="true" />
189
192
  <ContextUsageMeter usage={c.session?.contextUsage} />
@@ -196,6 +199,11 @@ export function ComposerFooter({ controller: c, onNotice }: {
196
199
  className="code-footer-effort" displayValue={selection.effort ?? 'Default'}
197
200
  options={[{ value: '', label: 'Native default' }, ...modelEfforts.map(value => ({ value, label: value }))]}
198
201
  disabled={controlsDisabled} onChange={value => void change({ effort: value || null })} />}
202
+ {selection.provider === 'claude' && <CodeFooterMenu label="Thinking" value={selection.thinking === false ? 'off' : 'on'}
203
+ className="code-footer-thinking"
204
+ options={[{ value: 'on', label: 'Thinking on', detail: 'Adaptive thinking with summaries' },
205
+ { value: 'off', label: 'Thinking off', detail: 'Answer without extended thinking' }]}
206
+ disabled={controlsDisabled} onChange={value => void change({ thinking: value === 'on' })} />}
199
207
  {locked && <button type="button" className="code-inline-action" onClick={c.newSession}>New session</button>}
200
208
  </div>
201
209
  {models.length === 0 && provider?.available && <div className="code-selection-notice">This runtime published no models.
@@ -110,7 +110,7 @@
110
110
  .code-composer-stop:focus-visible,
111
111
  .code-composer-send:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
112
112
 
113
- /* Footer: [runtime icon] [permission] ····· [model] [effort] */
113
+ /* Footer: [runtime icon] [permission] ····· [model] [effort] [thinking] */
114
114
  .code-composer-footer {
115
115
  display: flex;
116
116
  gap: 4px;
@@ -1,5 +1,6 @@
1
1
  import type { CodeCreateSessionRequest } from '../../../../src/code-mode/wire';
2
2
  import type { CodeDraft, CodeDraftBook } from './code-controller-drafts';
3
+ import type { CodeResendReason } from './code-types';
3
4
 
4
5
  const STORAGE_KEY = 'cli-jaw:code-drafts:v1';
5
6
  const WARNING_KEY = 'cli-jaw:code-draft-warnings:v1';
@@ -13,14 +14,14 @@ const STORAGE_WARNING = 'Draft recovery is unavailable in this tab. Your current
13
14
  const INCOMPLETE_WARNING = 'Draft recovery is incomplete: some recent text or request state was not saved for reload. Check your draft before sending.';
14
15
  const LIMIT_WARNING = 'Draft recovery is incomplete: some text was not saved for reload because this tab reached its recovery limit.';
15
16
 
16
- type DraftSelection = Pick<CodeCreateSessionRequest, 'provider' | 'cwd' | 'model' | 'effort' | 'permissionMode'>;
17
+ type DraftSelection = Pick<CodeCreateSessionRequest, 'provider' | 'cwd' | 'model' | 'effort' | 'permissionMode' | 'thinking'>;
17
18
  export interface StoredCodeDraft {
18
19
  input: string;
19
20
  edit: number;
20
21
  selection: DraftSelection;
21
22
  selectionEdit: number;
22
23
  creating: boolean;
23
- retry: { text: string; key: string; edit: number; resend?: boolean } | null;
24
+ retry: { text: string; key: string; edit: number; resend?: boolean; resendReason?: CodeResendReason } | null;
24
25
  stop: { turnId: string; epoch: number } | null;
25
26
  }
26
27
  export interface StoredCodeEndpoint {
@@ -41,18 +42,23 @@ function parseDraft(value: unknown): StoredCodeDraft | null {
41
42
  if (!object(selection) || !['codex-app', 'claude', 'cursor', 'grok'].includes(String(selection['provider']))
42
43
  || !text(selection['cwd'], 4096) || !text(selection['model'], 1024)
43
44
  || !(selection['effort'] === null || text(selection['effort'], 80))
44
- || !['ask', 'auto', 'read-only'].includes(String(selection['permissionMode']))) return null;
45
+ || !['ask', 'auto', 'read-only', 'plan', 'accept-edits', 'dont-ask', 'auto-review'].includes(String(selection['permissionMode']))
46
+ || !(selection['thinking'] === undefined || typeof selection['thinking'] === 'boolean')) return null;
45
47
  const retry = value['retry'], stop = value['stop'];
46
48
  if (retry !== null && (!object(retry) || !text(retry['text'], MAX_TEXT_CHARS)
47
49
  || !text(retry['key'], 240) || !retry['key'] || !count(retry['edit']) || retry['edit'] > value['edit'])) return null;
48
50
  if (stop !== null && (!object(stop) || !text(stop['turnId'], 240) || !stop['turnId'] || !count(stop['epoch']))) return null;
49
51
  return {
50
52
  input: value['input'], edit: value['edit'], selectionEdit: value['selectionEdit'], creating: value['creating'],
51
- selection: { provider: selection['provider'] as DraftSelection['provider'], cwd: selection['cwd'], model: selection['model'], effort: selection['effort'], permissionMode: selection['permissionMode'] as DraftSelection['permissionMode'] },
53
+ selection: { provider: selection['provider'] as DraftSelection['provider'], cwd: selection['cwd'], model: selection['model'], effort: selection['effort'], permissionMode: selection['permissionMode'] as DraftSelection['permissionMode'],
54
+ // Optional: drafts stored before the Thinking switch carry none.
55
+ ...(typeof selection['thinking'] === 'boolean' ? { thinking: selection['thinking'] } : {}) },
52
56
  // `resend` is optional on purpose: a draft stored before this field existed
53
57
  // must still parse, or recovery is wiped instead of downgraded.
54
58
  retry: retry === null ? null : { text: retry['text'] as string, key: retry['key'] as string, edit: retry['edit'] as number,
55
- ...(retry['resend'] === true ? { resend: true } : {}) },
59
+ ...(retry['resend'] === true ? { resend: true } : {}),
60
+ // An unknown reason downgrades to the generic spent-key copy instead of wiping recovery.
61
+ ...(retry['resend'] === true && retry['resendReason'] === 'rolled-back' ? { resendReason: 'rolled-back' as const } : {}) },
56
62
  stop: stop === null ? null : { turnId: stop['turnId'] as string, epoch: stop['epoch'] as number },
57
63
  };
58
64
  }
@@ -116,11 +122,13 @@ export function loadCodeDraftStorage(storage: Storage, endpoint: string): { data
116
122
  function pack(draft: CodeDraft): StoredCodeDraft | null {
117
123
  // Whitelist draft intent only. No permission answers, errors,
118
124
  // credentials, transcript, native cursor, metadata revision or live cursor.
119
- const { provider, cwd, model, effort, permissionMode } = draft.selection;
120
- return parseDraft({ input: draft.input, edit: draft.edit, selection: { provider, cwd, model, effort, permissionMode }, selectionEdit: draft.selectionEdit,
125
+ const { provider, cwd, model, effort, permissionMode, thinking } = draft.selection;
126
+ return parseDraft({ input: draft.input, edit: draft.edit, selection: { provider, cwd, model, effort, permissionMode,
127
+ ...(thinking === undefined ? {} : { thinking }) }, selectionEdit: draft.selectionEdit,
121
128
  creating: draft.createUnknown || draft.operation.kind === 'creating',
122
129
  retry: draft.retry ? { text: draft.retry.text, key: draft.retry.key, edit: draft.retry.edit,
123
- ...(draft.retry.resend ? { resend: true } : {}) } : null,
130
+ ...(draft.retry.resend ? { resend: true } : {}),
131
+ ...(draft.retry.resend && draft.retry.resendReason ? { resendReason: draft.retry.resendReason } : {}) } : null,
124
132
  stop: draft.stopTarget ? { turnId: draft.stopTarget.turnId, epoch: draft.stopTarget.epoch } : null });
125
133
  }
126
134
  export function saveCodeDraftStorage(storage: Storage, endpoint: string, book: CodeDraftBook): string | null {
@@ -1,6 +1,7 @@
1
1
  import type { CodeCreateSessionRequest, CodeModelCatalog, CodeSessionInfo } from '../../../../src/code-mode/wire';
2
2
  import type { CodeControllerModel } from './code-controller-types';
3
3
  import { browserCodeDraftStorage, loadCodeDraftStorage, saveCodeDraftStorage, type StoredCodeDraft } from './code-controller-draft-storage';
4
+ import { codeResendCopy, type CodeResendReason } from './code-types';
4
5
 
5
6
  export interface CodeDraft {
6
7
  input: string;
@@ -11,13 +12,30 @@ export interface CodeDraft {
11
12
  /**
12
13
  * `resend` marks an attempt whose key the server already spent. Acceptance is
13
14
  * then known — it failed — so the surfaces that describe an unconfirmed send
14
- * must stop hedging.
15
+ * must stop hedging. `resendReason` says why, when it was not the server's own settlement.
15
16
  */
16
- retry: { text: string; key: string; edit: number; resend?: boolean } | null;
17
+ retry: { text: string; key: string; edit: number; resend?: boolean; resendReason?: CodeResendReason } | null;
17
18
  createUnknown: boolean;
18
19
  requiredSequence: number;
19
20
  stopTarget: { turnId: string; epoch: number; outcome: 'pending' | 'unknown' | 'retryable' | 'accepted' } | null;
20
21
  permissionOperations: CodeControllerModel['permissionOperations'];
22
+ /**
23
+ * The clientTurnKey of a send whose turn has not visibly started or ended yet.
24
+ * Bridges the gap between pressing Send and the server reporting the turn, so
25
+ * the session reads as working the whole time. In-memory only: a reloaded page
26
+ * must not show a phantom spinner.
27
+ */
28
+ awaitingTurn: string | null;
29
+ /**
30
+ * A follow-up for the running Claude turn, captured with that turn's owner. In-memory only
31
+ * and never the send's `operation`/`retry`: it settles on its receipt or on its own
32
+ * `user_message`, and `unknown` means acceptance could not be confirmed (never resent).
33
+ */
34
+ steer: {
35
+ key: string; text: string; edit: number; turnId: string; epoch: number; state: 'sending' | 'unknown';
36
+ /** The server answered `steer_outcome_unknown`: Claude may have it, but the transcript never will. */
37
+ unrecorded?: boolean;
38
+ } | null;
21
39
  }
22
40
  export interface CodeDraftBook {
23
41
  endpoint: string;
@@ -37,7 +55,7 @@ const tabBooks = new WeakMap<Storage, Map<string, CodeDraftBook>>();
37
55
  export function createCodeDraft(selection: CodeCreateSessionRequest): CodeDraft {
38
56
  return { input: '', edit: 0, selection: { ...selection }, selectionEdit: 0,
39
57
  operation: { kind: 'idle', error: null }, retry: null, createUnknown: false, requiredSequence: 0,
40
- stopTarget: null, permissionOperations: {} };
58
+ stopTarget: null, permissionOperations: {}, awaitingTurn: null, steer: null };
41
59
  }
42
60
  function restoreDraft(saved: StoredCodeDraft): CodeDraft {
43
61
  const draft = createCodeDraft(saved.selection);
@@ -46,7 +64,7 @@ function restoreDraft(saved: StoredCodeDraft): CodeDraft {
46
64
  draft.retry = saved.retry;
47
65
  if (saved.creating) draft.operation = { kind: 'creating', error: 'Creation was interrupted by reload. The original session may exist; no request has been retried.' };
48
66
  if (saved.retry) draft.operation = { kind: 'unknown-send', error: saved.retry.resend
49
- ? 'The original attempt ended on the server without running. The message was not resent; Retry will submit it as a new message.'
67
+ ? codeResendCopy(saved.retry.resendReason).error
50
68
  : 'The original message has not been reconciled after reload. It will not be sent automatically.' };
51
69
  if (saved.stop) {
52
70
  draft.stopTarget = { ...saved.stop, outcome: 'unknown' };
@@ -75,13 +93,16 @@ export function persistCodeDraftBook(book: CodeDraftBook): void {
75
93
  }
76
94
  export function sessionSelection(session: CodeSessionInfo): CodeCreateSessionRequest {
77
95
  return { provider: session.provider, cwd: session.cwd, model: session.model,
78
- effort: session.effort, permissionMode: session.permissionMode };
96
+ effort: session.effort, permissionMode: session.permissionMode,
97
+ ...(session.thinking === null ? {} : { thinking: session.thinking }) };
79
98
  }
80
99
  export function catalogSelection(catalog: CodeModelCatalog, previous: CodeCreateSessionRequest, changedProvider = false): CodeCreateSessionRequest {
81
100
  const provider = catalog.providers.find(row => row.id === previous.provider);
82
101
  if (!provider) return previous;
83
102
  const capabilities = provider.capabilities;
84
- return { ...previous,
103
+ // Thinking is a Claude switch; it must not travel into a create for another runtime.
104
+ const { thinking, ...rest } = previous;
105
+ return { ...rest, ...(provider.id === 'claude' && thinking !== undefined ? { thinking } : {}),
85
106
  model: !changedProvider && provider.models.includes(previous.model) ? previous.model : provider.defaultModel,
86
107
  effort: !changedProvider && previous.effort !== null && capabilities.efforts.includes(previous.effort)
87
108
  ? previous.effort : null,
@@ -91,6 +112,12 @@ export function catalogSelection(catalog: CodeModelCatalog, previous: CodeCreate
91
112
  : capabilities.permissionModes.includes('auto') ? 'auto' : 'read-only',
92
113
  };
93
114
  }
115
+ /** The follow-up is in the transcript: clear the input only if it was not edited since. */
116
+ export function acknowledgeCodeSteer(draft: CodeDraft, key: string): void {
117
+ if (draft.steer?.key !== key) return;
118
+ if (draft.edit === draft.steer.edit) { draft.input = ''; draft.edit++; }
119
+ draft.steer = null;
120
+ }
94
121
  export function acknowledgeCodeSend(draft: CodeDraft, key: string): void {
95
122
  if (draft.retry?.key !== key) return;
96
123
  if (draft.edit === draft.retry.edit) { draft.input = ''; draft.edit++; }