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
@@ -0,0 +1,39 @@
1
+ // ── Resize Guard ──
2
+ // A viewport-resize burst (manager sidebar drag resizes the iframe, or a window
3
+ // edge drag) reflows every scrollable subtree each frame. Browser scroll
4
+ // anchoring then rewrites scrollTop per frame and content visibly jumps.
5
+ // While a burst is active `body.is-resizing` lets CSS suppress scroll
6
+ // anchoring so the page only reflows.
7
+
8
+ export const RESIZE_GUARD_SETTLE_MS = 160;
9
+
10
+ export type ResizeGuardHandlers = {
11
+ onResizeStart: () => void;
12
+ onResizeEnd: () => void;
13
+ settleMs?: number;
14
+ };
15
+
16
+ export function createResizeGuard({ onResizeStart, onResizeEnd, settleMs = RESIZE_GUARD_SETTLE_MS }: ResizeGuardHandlers): () => void {
17
+ let active = false;
18
+ let settleTimer: ReturnType<typeof setTimeout> | undefined;
19
+ return () => {
20
+ if (!active) {
21
+ active = true;
22
+ onResizeStart();
23
+ }
24
+ clearTimeout(settleTimer);
25
+ settleTimer = setTimeout(() => {
26
+ active = false;
27
+ settleTimer = undefined;
28
+ onResizeEnd();
29
+ }, settleMs);
30
+ };
31
+ }
32
+
33
+ export function initResizeGuard(): void {
34
+ const notify = createResizeGuard({
35
+ onResizeStart: () => document.body.classList.add('is-resizing'),
36
+ onResizeEnd: () => document.body.classList.remove('is-resizing'),
37
+ });
38
+ window.addEventListener('resize', notify, { passive: true });
39
+ }
@@ -5,7 +5,7 @@ import { t } from './i18n.js';
5
5
  import { state } from '../state.js';
6
6
  import { ICONS } from '../icons.js';
7
7
  import { providerIcon, providerLabel } from '../provider-icons.js';
8
- import { describeCliProbe, describeNativeStartFailure, resolveQuotaWindowDisplay, type CliStatusInfo, type QuotaEntry } from './settings-types.js';
8
+ import { describeCliProbe, describeNativeStartFailure, hasUsableCliStatus, isCliStatusUsable, resolveQuotaWindowDisplay, type CliStatusInfo, type QuotaEntry } from './settings-types.js';
9
9
  import {
10
10
  buildAccountParts,
11
11
  normalizeQuotaWindowLabel,
@@ -34,6 +34,7 @@ let cliStatusLoadInFlight: Promise<void> | null = null;
34
34
  let cliStatusRefreshFeedbackTimer: number | null = null;
35
35
 
36
36
  const CLI_STATUS_COLLAPSED_KEY = 'cliStatusCollapsed';
37
+ const CLI_STATUS_UNDETECTED_EXPANDED_KEY = 'cliStatusUndetectedExpanded';
37
38
 
38
39
  export function isEmbeddedPreviewFrame(): boolean {
39
40
  try {
@@ -53,6 +54,16 @@ function saveCliStatusCollapsed(collapsed: boolean): void {
53
54
  catch { /* ignore */ }
54
55
  }
55
56
 
57
+ function readCliStatusUndetectedExpanded(): boolean {
58
+ try { return localStorage.getItem(CLI_STATUS_UNDETECTED_EXPANDED_KEY) === 'true'; }
59
+ catch { return false; }
60
+ }
61
+
62
+ function saveCliStatusUndetectedExpanded(expanded: boolean): void {
63
+ try { localStorage.setItem(CLI_STATUS_UNDETECTED_EXPANDED_KEY, expanded ? 'true' : 'false'); }
64
+ catch { /* ignore */ }
65
+ }
66
+
56
67
  function formatCliStatusRefreshTime(date: Date): string {
57
68
  return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`;
58
69
  }
@@ -295,7 +306,8 @@ function renderCliStatus(data: { cliStatus: Record<string, CliStatusInfo> | null
295
306
  copilot: { install: 'npm i -g copilot', auth: t('cli.copilot.authHint') },
296
307
  };
297
308
 
298
- let html = '';
309
+ const usableCards: string[] = [];
310
+ const undetectedCards: string[] = [];
299
311
 
300
312
  if (!cliStatus || typeof cliStatus !== 'object') {
301
313
  if (el) el.innerHTML = '<div style="color:var(--text-dim);font-size:11px">Failed to load CLI status</div>';
@@ -431,7 +443,7 @@ function renderCliStatus(data: { cliStatus: Record<string, CliStatusInfo> | null
431
443
  ? `<div role="alert" style="font-size:10px;color:var(--warning);margin:2px 0 0 16px">${escapeHtml(startFailure.message)}</div>`
432
444
  : '';
433
445
 
434
- html += `
446
+ const cardHtml = `
435
447
  <div class="settings-group" style="margin-bottom:6px;padding:8px 10px">
436
448
  <div class="cli-status-row" style="display:flex;align-items:center">
437
449
  <span class="cli-dot ${dotClass}"></span>
@@ -445,24 +457,32 @@ function renderCliStatus(data: { cliStatus: Record<string, CliStatusInfo> | null
445
457
  ${windowsHtml}
446
458
  </div>
447
459
  `;
460
+ (isCliStatusUsable(info, q) ? usableCards : undetectedCards).push(cardHtml);
461
+ }
462
+
463
+ let html = usableCards.join('');
464
+ if (undetectedCards.length) {
465
+ // Collapsed by default: CLIs needing install/auth stop crowding the
466
+ // working list, but stay one click away in the same order.
467
+ const undetectedExpanded = readCliStatusUndetectedExpanded();
468
+ html += `
469
+ <div class="cli-undetected-group">
470
+ <button type="button" id="cliUndetectedToggle" class="cli-status-header cli-undetected-toggle${undetectedExpanded ? ' expanded' : ''}"
471
+ aria-expanded="${undetectedExpanded}" aria-controls="cliUndetectedList">
472
+ ${escapeHtml(t('cli.undetectedGroup', { count: undetectedCards.length }))}
473
+ </button>
474
+ <div id="cliUndetectedList"${undetectedExpanded ? '' : ' style="display:none"'}>${undetectedCards.join('')}</div>
475
+ </div>
476
+ `;
448
477
  }
449
478
 
450
479
  if (el) el.innerHTML = html;
451
480
 
452
- const allEntries = Object.entries(cliStatus);
453
- // While probes are failing we cannot tell ready from unready, so the
454
- // "no ready CLI" alarm would be exactly the false alarm #277 reports —
455
- // the runtime worked the whole time.
456
- const anyProbeUnavailable = allEntries.some(([, info]) => {
457
- const state = describeCliProbe(info);
458
- return state === 'probe-failing' || state === 'unknown';
459
- });
460
- const hasReadyCli = allEntries.some(([name, info]) => {
461
- if (describeCliProbe(info) !== 'ready') return false;
462
- const q = quota?.[name];
463
- return !q || q.authenticated !== false;
464
- });
465
- if (!hasReadyCli && !anyProbeUnavailable && allEntries.length > 0 && el) {
481
+ const allEntries = Object.entries(cliStatus).filter(([name]) => !SIDEBAR_HIDDEN_CLIS.has(name));
482
+ // The alarm shares the partition's usable rule: a stale-but-preserved row
483
+ // or an unsettled probe is last-known-good, not evidence of absence —
484
+ // otherwise it false-alarms through every re-check window (#277).
485
+ if (allEntries.length > 0 && !hasUsableCliStatus(allEntries, quota) && el) {
466
486
  el.insertAdjacentHTML('afterbegin',
467
487
  `<div style="padding:8px 10px;margin-bottom:8px;background:var(--warning-dim);border:1px solid var(--warning);border-radius:6px;font-size:11px;color:var(--warning)">
468
488
  ${ICONS.warning} ${t('cli.noReadyCli')}
@@ -470,6 +490,18 @@ function renderCliStatus(data: { cliStatus: Record<string, CliStatusInfo> | null
470
490
  );
471
491
  }
472
492
 
493
+ const undetectedToggle = document.getElementById('cliUndetectedToggle');
494
+ if (undetectedToggle) {
495
+ undetectedToggle.addEventListener('click', () => {
496
+ const expanded = undetectedToggle.getAttribute('aria-expanded') !== 'true';
497
+ undetectedToggle.setAttribute('aria-expanded', String(expanded));
498
+ undetectedToggle.classList.toggle('expanded', expanded);
499
+ const list = document.getElementById('cliUndetectedList');
500
+ if (list) list.style.display = expanded ? 'block' : 'none';
501
+ saveCliStatusUndetectedExpanded(expanded);
502
+ });
503
+ }
504
+
473
505
  const kcBtn = document.getElementById('copilotKeychainBtn');
474
506
  if (kcBtn) {
475
507
  kcBtn.addEventListener('click', async () => {
@@ -3,7 +3,7 @@
3
3
  import type { RuntimeTransport } from '../../../src/shared/runtime-contract.js';
4
4
  import type { PresentationMode } from '../../../src/shared/presentation.js';
5
5
 
6
- export interface PerCliConfig { provider?: string; model?: string; effort?: string; transport?: RuntimeTransport; fastMode?: boolean; contextWindow?: boolean; contextWindowSize?: number; contextCompactLimit?: number; }
6
+ export interface PerCliConfig { provider?: string; model?: string; effort?: string; transport?: RuntimeTransport; fastMode?: boolean; }
7
7
  export interface TelegramConfig { enabled?: boolean; token?: string; allowedChatIds?: number[]; forwardAll?: boolean; mentionOnly?: boolean; }
8
8
  export interface DiscordConfig { enabled?: boolean; token?: string; guildId?: string; channelIds?: string[]; forwardAll?: boolean; allowBots?: boolean; mentionOnly?: boolean; }
9
9
  // Slack needs TWO distinctly-scoped tokens: the bot token drives the Web API,
@@ -93,6 +93,44 @@ export function describeCliProbe(info: CliStatusInfo): 'checking' | 'unknown' |
93
93
  return info.available === true && info.capabilityReady !== false ? 'ready' : 'unavailable';
94
94
  }
95
95
 
96
+ /**
97
+ * Sidebar partition: a card stays in the top group only while current or
98
+ * preserved evidence says the CLI is usable — installed, capability-ready and
99
+ * authenticated. `unavailable`/`capability-failed` rows demote, and a `ready`
100
+ * or `stale` row demotes only on concrete evidence (missing binary/capability
101
+ * or `authenticated === false` from either the status probe or the quota
102
+ * reader). Indeterminate probe states (checking/unknown, or failing with no
103
+ * prior snapshot) are not proof of absence, and a stale row is last-known-good — a previously working
104
+ * CLI keeps its place until a fresh probe says otherwise.
105
+ */
106
+ export function isCliStatusUsable(info: CliStatusInfo, quota: QuotaEntry | null | undefined): boolean {
107
+ const probe = describeCliProbe(info);
108
+ if (probe === 'checking' || probe === 'unknown') return true;
109
+ if (probe === 'probe-failing') {
110
+ // A failing probe overlays the last successful snapshot; its concrete
111
+ // install/auth facts still hold, only a cold (null) row is unknown.
112
+ if (info.available == null) return true;
113
+ return info.available === true && info.capabilityReady !== false && info.authenticated !== false;
114
+ }
115
+ if (probe === 'unavailable' || probe === 'capability-failed') return false;
116
+ if (info.available !== true || info.capabilityReady === false) return false;
117
+ return info.authenticated !== false && quota?.authenticated !== false;
118
+ }
119
+
120
+ /**
121
+ * "Any usable CLI" rule the sidebar banner shares with the partition: true
122
+ * while at least one row reads usable under {@link isCliStatusUsable}. An
123
+ * alarm gated on this cannot false-fire through the stale re-check window or
124
+ * the initial probe pass — those rows count as usable, not as evidence of
125
+ * absence (#277).
126
+ */
127
+ export function hasUsableCliStatus(entries: Iterable<readonly [string, CliStatusInfo]>, quota: Record<string, QuotaEntry> | null | undefined): boolean {
128
+ for (const [name, info] of entries) {
129
+ if (isCliStatusUsable(info, quota?.[name])) return true;
130
+ }
131
+ return false;
132
+ }
133
+
96
134
  export type CliProbeAvailabilityPresentation = {
97
135
  kind: 'checking' | 'unknown' | 'failing' | 'none';
98
136
  message: string | null;
@@ -0,0 +1,143 @@
1
+ // ── Inline `$skill` mentions ──
2
+ // Typing `$` anywhere in the composer lists the active skills; picking one
3
+ // inserts `$<id> ` at the caret. The server expands the mention when the
4
+ // message runs (src/core/skill-mentions.ts); the chat keeps the typed text.
5
+ import { state } from '../state.js';
6
+ import { fetchWithLocale } from './i18n.js';
7
+ import { escapeHtml } from '../render.js';
8
+ import { isDropdownOpen as isSlashDropdownOpen } from './slash-commands.js';
9
+ import { findSkillMentionToken, rankSkillMentions, skillMentionKey, type SkillMentionToken } from '../../../src/shared/skill-mention.js';
10
+
11
+ interface ApiSkill { id: string; mention?: string | undefined; description?: string | undefined; enabled?: boolean | undefined }
12
+ /** `name` carries the server-computed mention word so skillMentionKey() returns it. */
13
+ interface MentionSkill { id: string; name: string; description?: string | undefined }
14
+
15
+ let filtered: MentionSkill[] = [];
16
+ let selectedIdx = 0;
17
+ let isOpen = false;
18
+ let token: SkillMentionToken | null = null;
19
+ let loading: Promise<void> | null = null;
20
+ /** Bumped by every update() and close(): an older async update never renders. */
21
+ let updateSeq = 0;
22
+
23
+ const dropdown = (): HTMLElement | null => document.getElementById('skillMentionDropdown');
24
+ const input = (): HTMLTextAreaElement | null => document.getElementById('chatInput') as HTMLTextAreaElement | null;
25
+
26
+ function activeSkills(): MentionSkill[] {
27
+ return (state.allSkills as ApiSkill[])
28
+ .filter(skill => skill && skill.enabled && typeof skill.id === 'string')
29
+ .map(skill => ({ id: skill.id, name: skill.mention || skill.id, description: skill.description }));
30
+ }
31
+
32
+ /** The skills panel owns `state.allSkills`; load it once if that panel never opened. */
33
+ function ensureSkillsLoaded(): Promise<void> {
34
+ if ((state.allSkills as unknown[]).length) return Promise.resolve();
35
+ loading ??= fetchWithLocale('/api/skills')
36
+ .then(res => res.json())
37
+ .then((list: unknown) => {
38
+ if (Array.isArray(list) && !(state.allSkills as unknown[]).length) state.allSkills = list;
39
+ })
40
+ .catch(() => { /* popup stays closed; sending does not depend on it */ })
41
+ .finally(() => { loading = null; });
42
+ return loading;
43
+ }
44
+
45
+ /** The `/` list fades for 150ms after it closes; clear it so the two never stack. */
46
+ function hideFadingSlashList(): void {
47
+ const slash = document.getElementById('cmdDropdown');
48
+ if (!slash) return;
49
+ slash.classList.remove('visible');
50
+ slash.style.display = 'none';
51
+ }
52
+
53
+ function render(): void {
54
+ const el = dropdown();
55
+ const inp = input();
56
+ if (!el || !inp) return;
57
+ hideFadingSlashList();
58
+ el.innerHTML = filtered.map((skill, i) => `<div class="cmd-item${i === selectedIdx ? ' selected' : ''}"
59
+ role="option" id="skill-mention-item-${i}" aria-selected="${i === selectedIdx}" data-index="${i}">
60
+ <span class="cmd-name">$${escapeHtml(skillMentionKey(skill))}</span>
61
+ <span class="cmd-desc">${escapeHtml(skill.description || '')}</span>
62
+ </div>`).join('');
63
+ el.style.display = 'block';
64
+ requestAnimationFrame(() => el.classList.add('visible'));
65
+ isOpen = true;
66
+ inp.setAttribute('aria-controls', 'skillMentionDropdown');
67
+ inp.setAttribute('aria-expanded', 'true');
68
+ inp.setAttribute('aria-activedescendant', `skill-mention-item-${selectedIdx}`);
69
+ el.querySelector('.selected')?.scrollIntoView({ block: 'nearest' });
70
+ }
71
+
72
+ export function close(): void {
73
+ updateSeq++; // also cancels an update() still awaiting the skill list
74
+ if (!isOpen) return;
75
+ isOpen = false; filtered = []; selectedIdx = 0; token = null;
76
+ const el = dropdown();
77
+ if (el) { el.classList.remove('visible'); el.style.display = 'none'; el.innerHTML = ''; }
78
+ const inp = input();
79
+ if (!inp) return;
80
+ // The textarea points back at the `/` list either way; if that list just
81
+ // opened on the same frame it owns expanded/activedescendant, so leave those.
82
+ inp.setAttribute('aria-controls', 'cmdDropdown');
83
+ if (!isSlashDropdownOpen()) {
84
+ inp.setAttribute('aria-expanded', 'false');
85
+ inp.setAttribute('aria-activedescendant', '');
86
+ }
87
+ }
88
+
89
+ export async function update(inp: HTMLTextAreaElement | null): Promise<void> {
90
+ const seq = ++updateSeq;
91
+ if (!inp || isSlashDropdownOpen() || inp.selectionStart !== inp.selectionEnd) { close(); return; }
92
+ if (!findSkillMentionToken(inp.value, inp.selectionStart ?? inp.value.length)) { close(); return; }
93
+ await ensureSkillsLoaded();
94
+ if (seq !== updateSeq) return; // a newer keystroke, a close() or a send owns the popup now
95
+ // Re-read after the await: the text or caret may have moved while the list loaded.
96
+ const next = findSkillMentionToken(inp.value, inp.selectionStart ?? inp.value.length);
97
+ if (!next || isSlashDropdownOpen()) { close(); return; }
98
+ const ranked = rankSkillMentions(activeSkills(), next.query);
99
+ if (!ranked.length) { close(); return; }
100
+ token = next; filtered = ranked; selectedIdx = 0;
101
+ render();
102
+ }
103
+
104
+ function applySelection(): void {
105
+ const skill = filtered[selectedIdx];
106
+ const inp = input();
107
+ const at = token;
108
+ close();
109
+ if (!skill || !inp || !at) return;
110
+ const caret = inp.selectionStart ?? inp.value.length;
111
+ const inserted = `$${skillMentionKey(skill)} `;
112
+ inp.value = inp.value.slice(0, at.start) + inserted + inp.value.slice(caret);
113
+ const pos = at.start + inserted.length;
114
+ inp.focus();
115
+ inp.selectionStart = inp.selectionEnd = pos;
116
+ inp.dispatchEvent(new Event('input', { bubbles: true })); // auto-resize + draft listeners
117
+ }
118
+
119
+ export function handleKeydown(e: KeyboardEvent): boolean {
120
+ if (!isOpen) return false;
121
+ if (e.isComposing) return false;
122
+ if (e.key === 'ArrowDown') { e.preventDefault(); selectedIdx = Math.min(filtered.length - 1, selectedIdx + 1); render(); return true; }
123
+ if (e.key === 'ArrowUp') { e.preventDefault(); selectedIdx = Math.max(0, selectedIdx - 1); render(); return true; }
124
+ if (e.key === 'Tab' || (e.key === 'Enter' && !e.shiftKey)) { e.preventDefault(); applySelection(); return true; }
125
+ if (e.key === 'Escape') { e.preventDefault(); close(); return true; }
126
+ return false; // caret keys fall through; the keyup listener re-evaluates the token
127
+ }
128
+
129
+ export function handleClick(e: Event): void {
130
+ const item = (e.target as HTMLElement).closest('.cmd-item') as HTMLElement | null;
131
+ if (!item) return;
132
+ const idx = Number.parseInt(item.dataset['index'] || '-1', 10);
133
+ if (Number.isNaN(idx) || idx < 0 || idx >= filtered.length) return;
134
+ selectedIdx = idx;
135
+ applySelection();
136
+ }
137
+
138
+ export function handleOutsideClick(e: Event): void {
139
+ if (!isOpen) return;
140
+ const el = dropdown();
141
+ // Same test as the `/` list: clicks in the textarea are caret moves, re-evaluated by update().
142
+ if (el && !el.contains(e.target as Node) && e.target !== input()) close();
143
+ }
package/public/js/main.ts CHANGED
@@ -45,6 +45,10 @@ import {
45
45
  loadCommands, update as updateSlashDropdown, handleKeydown as handleSlashKeydown,
46
46
  handleClick as handleSlashClick, handleOutsideClick as handleSlashOutsideClick,
47
47
  } from './features/slash-commands.js';
48
+ import {
49
+ update as updateSkillMentions, handleKeydown as handleSkillMentionKeydown,
50
+ handleClick as handleSkillMentionClick, handleOutsideClick as handleSkillMentionOutsideClick,
51
+ } from './features/skill-mentions.js';
48
52
  import { toggleSkill, filterSkills, searchSkills } from './features/skills.js';
49
53
  import {
50
54
  loadSettings, setPerm, onCliChange,
@@ -77,6 +81,7 @@ import { loadCliRegistry } from './constants.js';
77
81
  import { initAppName } from './features/appname.js';
78
82
  import { initAvatar } from './features/avatar.js';
79
83
  import { initSidebar, toggleLeft, toggleRight } from './features/sidebar.js';
84
+ import { initResizeGuard } from './features/resize-guard.js';
80
85
  import { initTheme } from './features/theme.js';
81
86
  import { initPreviewShortcutBridge } from './features/preview-shortcut-bridge.js';
82
87
  import { ensurePreviewCapabilityListener, ensurePreviewInsertTextListener } from './preview-parent-origin.js';
@@ -117,6 +122,7 @@ function isVoiceRecordingShortcut(e: KeyboardEvent): boolean {
117
122
  document.getElementById('btnSend')?.addEventListener('click', () => { void sendMessage('button'); });
118
123
  const chatInput = document.getElementById('chatInput') as HTMLTextAreaElement | null;
119
124
  chatInput?.addEventListener('keydown', (e) => {
125
+ if (handleSkillMentionKeydown(e as KeyboardEvent)) return;
120
126
  if (handleSlashKeydown(e as KeyboardEvent)) return;
121
127
  handleKey(e as KeyboardEvent);
122
128
  });
@@ -126,6 +132,7 @@ chatInput?.addEventListener('input', (e) => {
126
132
  if (slashInputRaf) cancelAnimationFrame(slashInputRaf);
127
133
  slashInputRaf = requestAnimationFrame(() => {
128
134
  updateSlashDropdown((e.target as HTMLTextAreaElement)?.value || '');
135
+ void updateSkillMentions(e.target as HTMLTextAreaElement | null);
129
136
  slashInputRaf = 0;
130
137
  });
131
138
  });
@@ -134,6 +141,13 @@ chatInput?.addEventListener('cmd-execute', () => {
134
141
  });
135
142
  document.getElementById('cmdDropdown')?.addEventListener('click', handleSlashClick);
136
143
  document.addEventListener('click', handleSlashOutsideClick);
144
+ document.getElementById('skillMentionDropdown')?.addEventListener('click', handleSkillMentionClick);
145
+ document.addEventListener('click', handleSkillMentionOutsideClick);
146
+ // Caret moves without typing (click, arrows, Home/End) re-evaluate the `$` token under the caret.
147
+ chatInput?.addEventListener('click', () => { void updateSkillMentions(chatInput); });
148
+ chatInput?.addEventListener('keyup', (e) => {
149
+ if (['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes((e as KeyboardEvent).key)) void updateSkillMentions(chatInput);
150
+ });
137
151
  initPendingQueue();
138
152
  initBgtaskBadge();
139
153
  document.getElementById('filePreviewClear')?.addEventListener('click', clearAttachedFiles);
@@ -462,6 +476,7 @@ async function bootstrap(): Promise<void> {
462
476
  if (isCliStatusExpanded()) loadCliStatus();
463
477
  // loadMessages() is handled by ws.js onopen (clear + reload)
464
478
  initSidebar();
479
+ initResizeGuard();
465
480
  loadEmployees();
466
481
  initHeartbeatBadge();
467
482
  void refreshMemorySidebar();
@@ -14,6 +14,8 @@ const THRESHOLD = 1;
14
14
  const EST_HEIGHT = 80;
15
15
  const OVERSCAN = 5;
16
16
  const BOTTOM_THRESHOLD = 80;
17
+ /** Quiet period after the last width change before a resize drag counts as done. */
18
+ const RESIZE_SETTLE_MS = 160;
17
19
 
18
20
  export type RestoreReason =
19
21
  | 'pageshow'
@@ -45,6 +47,13 @@ interface ScrollAnchor {
45
47
  el: HTMLElement;
46
48
  top: number;
47
49
  }
50
+ /** Where the reader was when a resize drag began. Captured once per drag so
51
+ * per-frame corrections never inherit the previous frame's drift. */
52
+ interface ResizePin {
53
+ follow: boolean;
54
+ anchor: { index: number; top: number } | null;
55
+ settleTimer: number;
56
+ }
48
57
 
49
58
  function readMeasuredHeight(el: MeasurableVirtualElement): number {
50
59
  const height = Math.ceil(el.getBoundingClientRect().height);
@@ -84,6 +93,7 @@ export class VirtualScroll {
84
93
  private itemGap = 0;
85
94
  private restorePassTimers = new Set<number>();
86
95
  private shouldFollowAfterRestore: RestoreFollowPredicate = () => true;
96
+ private resizePin: ResizePin | null = null;
87
97
 
88
98
  onLazyRender: LazyRenderCallback | null = null;
89
99
  onPostRender: ((viewport: HTMLElement) => void) | null = null;
@@ -182,6 +192,56 @@ export class VirtualScroll {
182
192
  this.renderItems();
183
193
  }
184
194
 
195
+ /** Width-driven re-measure that keeps the reader's place for a whole drag.
196
+ * Follow intent and the anchor are read once, at the first frame: later
197
+ * frames see scroll events caused by the reflow itself, and TanStack's own
198
+ * item measurements land a frame after ours, so re-reading either would
199
+ * compound the drift this exists to remove. */
200
+ private invalidateLayoutPinned(): void {
201
+ if (!this.virtualizer) return;
202
+ let pin = this.resizePin;
203
+ if (!pin) {
204
+ const follow = this.shouldFollowAfterRestore();
205
+ pin = { follow, anchor: follow ? null : this.captureIndexAnchor(), settleTimer: 0 };
206
+ this.resizePin = pin;
207
+ }
208
+ window.clearTimeout(pin.settleTimer);
209
+ this.invalidateLayout();
210
+ this.applyResizePin(pin);
211
+ const settling = pin;
212
+ settling.settleTimer = window.setTimeout(() => {
213
+ requestAnimationFrame(() => {
214
+ if (this.resizePin !== settling) return;
215
+ this.resizePin = null;
216
+ if (this.virtualizer) this.applyResizePin(settling);
217
+ });
218
+ }, RESIZE_SETTLE_MS);
219
+ }
220
+
221
+ private applyResizePin(pin: ResizePin): void {
222
+ if (pin.follow) {
223
+ this.scrollToBottom();
224
+ return;
225
+ }
226
+ if (!pin.anchor) return;
227
+ const el = this.mounted.get(pin.anchor.index);
228
+ if (!el?.isConnected) return;
229
+ const delta = el.getBoundingClientRect().top - pin.anchor.top;
230
+ if (Number.isFinite(delta) && delta !== 0) this.container.scrollTop += delta;
231
+ }
232
+
233
+ private captureIndexAnchor(): ResizePin['anchor'] {
234
+ const el = this.firstVisibleMountedItem();
235
+ if (!el) return null;
236
+ const index = Number(el.dataset['vsIdx'] ?? '-1');
237
+ return index >= 0 ? { index, top: el.getBoundingClientRect().top } : null;
238
+ }
239
+
240
+ private clearResizePin(): void {
241
+ if (this.resizePin) window.clearTimeout(this.resizePin.settleTimer);
242
+ this.resizePin = null;
243
+ }
244
+
185
245
  /** Bulk-load items. Call AFTER registering onLazyRender/onPostRender. */
186
246
  setItems(
187
247
  items: VirtualItem[],
@@ -483,11 +543,16 @@ export class VirtualScroll {
483
543
  // TanStack's ResizeObserver watches individual items, but not the
484
544
  // container width — a window resize doesn't trigger item RO callbacks
485
545
  // because items are position:absolute (width from left:0 + right:0).
546
+ // Re-measuring alone leaves scrollTop wherever the height deltas put it,
547
+ // so a drag (Manager sidebar, window edge) drifts a few px per frame
548
+ // until chat-scroll reads the gap as "user scrolled up" and stops
549
+ // following. Re-pin every pass: bottom while following, else the first
550
+ // visible row's on-screen top.
486
551
  let resizeRaf = 0;
487
552
  const scheduleInvalidateLayout = () => {
488
553
  cancelAnimationFrame(resizeRaf);
489
554
  resizeRaf = requestAnimationFrame(() => {
490
- this.invalidateLayout();
555
+ this.invalidateLayoutPinned();
491
556
  });
492
557
  };
493
558
  const onResize = () => scheduleInvalidateLayout();
@@ -495,6 +560,7 @@ export class VirtualScroll {
495
560
  cleanupFns.push(() => {
496
561
  window.removeEventListener('resize', onResize);
497
562
  cancelAnimationFrame(resizeRaf);
563
+ this.clearResizePin();
498
564
  });
499
565
 
500
566
  if (typeof ResizeObserver !== 'undefined') {
@@ -317,9 +317,11 @@
317
317
  "cli.showMore": "More ▸",
318
318
  "cli.authRequired": "Install / auth required",
319
319
  "cli.notAuthenticated": "Auth required — run command below",
320
+ "cli.undetectedGroup": "Needs install or sign-in ({count})",
320
321
  "drag.drop": "Drop files here",
321
322
  "status.responding": "Responding",
322
323
  "aria.cmdList": "Command list",
324
+ "aria.skillMentionList": "Skill list",
323
325
  "btn.attach": "Attach file",
324
326
  "input.placeholder": "Type a message...",
325
327
  "queue.pendingTitle": "Pending",
@@ -306,9 +306,11 @@
306
306
  "cli.showMore": "もっと見る ▸",
307
307
  "cli.authRequired": "インストール/認証が必要です",
308
308
  "cli.notAuthenticated": "認証が必要です — 下のコマンドを実行してください",
309
+ "cli.undetectedGroup": "インストール・ログインが必要 ({count})",
309
310
  "drag.drop": "ここにファイルをドロップ",
310
311
  "status.responding": "応答中",
311
312
  "aria.cmdList": "コマンド一覧",
313
+ "aria.skillMentionList": "スキル一覧",
312
314
  "btn.attach": "ファイルを添付",
313
315
  "input.placeholder": "メッセージを入力...",
314
316
  "queue.pendingTitle": "待機中",
@@ -317,9 +317,11 @@
317
317
  "cli.showMore": "더 보기 ▸",
318
318
  "cli.authRequired": "설치 / 인증 필요",
319
319
  "cli.notAuthenticated": "인증 필요 — 아래 명령어 실행",
320
+ "cli.undetectedGroup": "설치·로그인 필요 ({count})",
320
321
  "drag.drop": "파일을 여기에 드랍하세요",
321
322
  "status.responding": "응답 중",
322
323
  "aria.cmdList": "커맨드 목록",
324
+ "aria.skillMentionList": "스킬 목록",
323
325
  "btn.attach": "파일 첨부",
324
326
  "input.placeholder": "메시지 입력...",
325
327
  "queue.pendingTitle": "대기 중",
@@ -306,9 +306,11 @@
306
306
  "cli.showMore": "更多 ▸",
307
307
  "cli.authRequired": "需要安装/认证",
308
308
  "cli.notAuthenticated": "需要认证 — 请运行下面的命令",
309
+ "cli.undetectedGroup": "需要安装或登录 ({count})",
309
310
  "drag.drop": "将文件拖到这里",
310
311
  "status.responding": "正在回复",
311
312
  "aria.cmdList": "命令列表",
313
+ "aria.skillMentionList": "技能列表",
312
314
  "btn.attach": "附加文件",
313
315
  "input.placeholder": "输入消息...",
314
316
  "queue.pendingTitle": "待处理",
@@ -22,6 +22,7 @@ import { useWindowFullscreen } from './hooks/useWindowFullscreen';
22
22
  import type { useDashboardView } from './hooks/useDashboardView';
23
23
  import type { DashboardDetailTab, DashboardInstance, DashboardNotesAuthoringMode, DashboardNotesGraphSettings, DashboardNotesViewMode, DashboardScanResult, DashboardShortcutAction, ManagerEvent, NoteMetadata } from './types';
24
24
  import { createManagerCaptureKeydownHandler } from './manager-shortcut-runner';
25
+ import { normalizeManagerShortcutKeymap } from './manager-shortcuts';
25
26
 
26
27
  type AppChromeProps = {
27
28
  view: ReturnType<typeof useDashboardView>;
@@ -158,6 +159,7 @@ export function AppChrome(props: AppChromeProps) {
158
159
  dashboardSettingsUi={props.dashboardSettingsUi} titleSupport={props.titleSupport} onDashboardSettingsPatch={props.handleDashboardSettingsPatch}
159
160
  viewMode={props.view.viewMode} onViewModeChange={props.view.setViewMode}
160
161
  port={Number(window.location.port) || 3457} workingDir={props.view.rightFolderRootPath || ''}
162
+ codeNewSessionShortcut={normalizeManagerShortcutKeymap(props.view.dashboardShortcutKeymap).newCodeSession}
161
163
  query={props.query} onQueryChange={props.setQuery} onSelectInstance={props.handleSelectInstance} />
162
164
  )}
163
165
  activityHeight={props.view.activityDockCollapsed ? 48 : props.view.activityDockHeight}
@@ -281,7 +281,7 @@ export function InstancePreview(props: InstancePreviewProps) {
281
281
  // Keep the dedicated preview origin as the default. The legacy `/i`
282
282
  // path is only a fallback because root-relative `/api` and `/ws`
283
283
  // requests must keep targeting the managed instance.
284
- { theme: props.theme },
284
+ { theme: props.theme, desktop: isElectron() },
285
285
  );
286
286
  const disabledReason = props.instance?.ok
287
287
  ? 'Preview is off. Turn it on from the header to mount the iframe.'
@@ -176,6 +176,8 @@ type Props = {
176
176
  onViewModeChange: (mode: DashboardViewMode) => void;
177
177
  port: number;
178
178
  workingDir: string;
179
+ /** Resolved `newCodeSession` chord, shown as the sidebar button's hint. */
180
+ codeNewSessionShortcut?: string;
179
181
  query: string;
180
182
  onQueryChange: (value: string) => void;
181
183
  onSelectInstance: (instance: DashboardInstance) => void;
@@ -607,7 +609,7 @@ export function SidebarRailRouter(props: Props) {
607
609
  {props.viewMode === 'code' && props.sidebarMode === 'instances' ? (
608
610
  <WorkspaceSurface active>
609
611
  <Suspense fallback={<div style={{ padding: '24px', color: 'var(--text-dim)', fontSize: '13px' }}>Loading Code workspace...</div>}>
610
- <CodeCanvas port={props.port} workingDir={codeWorkingDir} onWorkingDirChange={handleCodeWorkingDirChange} onOpenLocalFile={handleRightPreviewFile} />
612
+ <CodeCanvas port={props.port} workingDir={codeWorkingDir} onWorkingDirChange={handleCodeWorkingDirChange} onOpenLocalFile={handleRightPreviewFile} newSessionShortcut={props.codeNewSessionShortcut} />
611
613
  </Suspense>
612
614
  </WorkspaceSurface>
613
615
  ) : null}