@antelopejs/dms-ai 0.1.6 → 0.2.1

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 (565) hide show
  1. package/README.md +50 -12
  2. package/dist/channels/bridge.d.ts +6 -1
  3. package/dist/channels/bridge.js +29 -1
  4. package/dist/channels/bridge.js.map +1 -1
  5. package/dist/channels/channel-service.d.ts +2 -1
  6. package/dist/channels/channel-service.js +3 -2
  7. package/dist/channels/channel-service.js.map +1 -1
  8. package/dist/channels/sse-stream.d.ts +3 -2
  9. package/dist/config.d.ts +0 -2
  10. package/dist/config.js +0 -7
  11. package/dist/config.js.map +1 -1
  12. package/dist/constants/http.d.ts +9 -0
  13. package/dist/constants/http.js +14 -0
  14. package/dist/constants/http.js.map +1 -0
  15. package/dist/constants/i18n.d.ts +11 -0
  16. package/dist/constants/i18n.js +15 -0
  17. package/dist/constants/i18n.js.map +1 -0
  18. package/dist/constants/pages.d.ts +50 -0
  19. package/dist/constants/pages.js +58 -0
  20. package/dist/constants/pages.js.map +1 -0
  21. package/dist/constants/sidecar.d.ts +0 -1
  22. package/dist/constants/sidecar.js +1 -2
  23. package/dist/constants/sidecar.js.map +1 -1
  24. package/dist/index.js +0 -8
  25. package/dist/index.js.map +1 -1
  26. package/dist/lifecycle/sidecar-owner.d.ts +1 -0
  27. package/dist/lifecycle/sidecar-owner.js +13 -0
  28. package/dist/lifecycle/sidecar-owner.js.map +1 -1
  29. package/dist/lifecycle/spawn-sidecar.d.ts +1 -0
  30. package/dist/lifecycle/spawn-sidecar.js +10 -10
  31. package/dist/lifecycle/spawn-sidecar.js.map +1 -1
  32. package/dist/logging/log-buffer.d.ts +0 -6
  33. package/dist/logging/log-buffer.js +0 -1
  34. package/dist/logging/log-buffer.js.map +1 -1
  35. package/dist/pages/activity/columns.d.ts +2 -0
  36. package/dist/pages/activity/columns.js +103 -0
  37. package/dist/pages/activity/columns.js.map +1 -0
  38. package/dist/pages/activity/page.d.ts +1 -2
  39. package/dist/pages/activity/page.js +79 -44
  40. package/dist/pages/activity/page.js.map +1 -1
  41. package/dist/pages/all.d.ts +1 -0
  42. package/dist/pages/all.js +1 -0
  43. package/dist/pages/all.js.map +1 -1
  44. package/dist/pages/changes/page.d.ts +9 -0
  45. package/dist/pages/changes/page.js +22 -0
  46. package/dist/pages/changes/page.js.map +1 -0
  47. package/dist/pages/meta.d.ts +9 -0
  48. package/dist/pages/meta.js +31 -0
  49. package/dist/pages/meta.js.map +1 -0
  50. package/dist/pages/module.js +9 -5
  51. package/dist/pages/module.js.map +1 -1
  52. package/dist/pages/overview/insights.d.ts +3 -0
  53. package/dist/pages/overview/insights.js +77 -0
  54. package/dist/pages/overview/insights.js.map +1 -0
  55. package/dist/pages/overview/kpis.d.ts +2 -0
  56. package/dist/pages/overview/kpis.js +39 -0
  57. package/dist/pages/overview/kpis.js.map +1 -0
  58. package/dist/pages/overview/page.d.ts +6 -3
  59. package/dist/pages/overview/page.js +19 -62
  60. package/dist/pages/overview/page.js.map +1 -1
  61. package/dist/pages/overview/recent-changes.d.ts +2 -0
  62. package/dist/pages/overview/recent-changes.js +144 -0
  63. package/dist/pages/overview/recent-changes.js.map +1 -0
  64. package/dist/pages/overview/status.d.ts +3 -0
  65. package/dist/pages/overview/status.js +36 -0
  66. package/dist/pages/overview/status.js.map +1 -0
  67. package/dist/pages/settings/availability.d.ts +2 -0
  68. package/dist/pages/settings/availability.js +46 -0
  69. package/dist/pages/settings/availability.js.map +1 -0
  70. package/dist/pages/settings/options.d.ts +10 -0
  71. package/dist/pages/settings/options.js +58 -0
  72. package/dist/pages/settings/options.js.map +1 -0
  73. package/dist/pages/settings/page.d.ts +2 -1
  74. package/dist/pages/settings/page.js +39 -13
  75. package/dist/pages/settings/page.js.map +1 -1
  76. package/dist/pages/settings/sections.d.ts +2 -0
  77. package/dist/pages/settings/sections.js +164 -0
  78. package/dist/pages/settings/sections.js.map +1 -0
  79. package/dist/pages/skills/page.d.ts +2 -1
  80. package/dist/pages/skills/page.js +120 -12
  81. package/dist/pages/skills/page.js.map +1 -1
  82. package/dist/routes/activity-csv.d.ts +2 -0
  83. package/dist/routes/activity-csv.js +42 -0
  84. package/dist/routes/activity-csv.js.map +1 -0
  85. package/dist/routes/activity.d.ts +8 -0
  86. package/dist/routes/activity.js +105 -0
  87. package/dist/routes/activity.js.map +1 -0
  88. package/dist/routes/actor.d.ts +2 -0
  89. package/dist/routes/actor.js +8 -0
  90. package/dist/routes/actor.js.map +1 -0
  91. package/dist/routes/banner.d.ts +3 -0
  92. package/dist/routes/banner.js +9 -0
  93. package/dist/routes/banner.js.map +1 -0
  94. package/dist/routes/builder.js +1 -5
  95. package/dist/routes/builder.js.map +1 -1
  96. package/dist/routes/changes.d.ts +17 -0
  97. package/dist/routes/changes.js +152 -0
  98. package/dist/routes/changes.js.map +1 -0
  99. package/dist/routes/channels.js +2 -1
  100. package/dist/routes/channels.js.map +1 -1
  101. package/dist/routes/index.d.ts +3 -0
  102. package/dist/routes/index.js +3 -0
  103. package/dist/routes/index.js.map +1 -1
  104. package/dist/routes/list-query.d.ts +9 -0
  105. package/dist/routes/list-query.js +34 -0
  106. package/dist/routes/list-query.js.map +1 -0
  107. package/dist/routes/metric-shapes.d.ts +8 -0
  108. package/dist/routes/metric-shapes.js +107 -0
  109. package/dist/routes/metric-shapes.js.map +1 -0
  110. package/dist/routes/metrics.d.ts +5 -48
  111. package/dist/routes/metrics.js +52 -42
  112. package/dist/routes/metrics.js.map +1 -1
  113. package/dist/routes/settings.d.ts +3 -16
  114. package/dist/routes/settings.js +40 -14
  115. package/dist/routes/settings.js.map +1 -1
  116. package/dist/routes/sidecar-results.d.ts +6 -0
  117. package/dist/routes/sidecar-results.js +44 -0
  118. package/dist/routes/sidecar-results.js.map +1 -0
  119. package/dist/routes/skills.d.ts +4 -14
  120. package/dist/routes/skills.js +91 -6
  121. package/dist/routes/skills.js.map +1 -1
  122. package/dist/routes/status-shapes.d.ts +5 -0
  123. package/dist/routes/status-shapes.js +176 -0
  124. package/dist/routes/status-shapes.js.map +1 -0
  125. package/dist/routes/status.d.ts +9 -0
  126. package/dist/routes/status.js +92 -0
  127. package/dist/routes/status.js.map +1 -0
  128. package/dist/routes/undo-confirm.d.ts +3 -0
  129. package/dist/routes/undo-confirm.js +51 -0
  130. package/dist/routes/undo-confirm.js.map +1 -0
  131. package/dist/sidecar/http-client.d.ts +11 -0
  132. package/dist/sidecar/http-client.js +59 -0
  133. package/dist/sidecar/http-client.js.map +1 -0
  134. package/dist/sidecar/index.d.ts +2 -0
  135. package/dist/sidecar/index.js +19 -0
  136. package/dist/sidecar/index.js.map +1 -0
  137. package/dist/sidecar/query.d.ts +3 -0
  138. package/dist/sidecar/query.js +25 -0
  139. package/dist/sidecar/query.js.map +1 -0
  140. package/dist/types/activity.d.ts +31 -0
  141. package/dist/types/activity.js +3 -0
  142. package/dist/types/activity.js.map +1 -0
  143. package/dist/types/changes.d.ts +45 -0
  144. package/dist/types/changes.js +3 -0
  145. package/dist/types/changes.js.map +1 -0
  146. package/dist/types/index.d.ts +5 -0
  147. package/dist/types/index.js +5 -0
  148. package/dist/types/index.js.map +1 -1
  149. package/dist/types/metrics.d.ts +76 -0
  150. package/dist/types/metrics.js +3 -0
  151. package/dist/types/metrics.js.map +1 -0
  152. package/dist/types/skills.d.ts +51 -0
  153. package/dist/types/skills.js +3 -0
  154. package/dist/types/skills.js.map +1 -0
  155. package/dist/types/status.d.ts +16 -0
  156. package/dist/types/status.js +3 -0
  157. package/dist/types/status.js.map +1 -0
  158. package/dist/vocabulary/enumerations.d.ts +9 -0
  159. package/dist/vocabulary/enumerations.js +52 -0
  160. package/dist/vocabulary/enumerations.js.map +1 -0
  161. package/dist/vocabulary/index.d.ts +5 -0
  162. package/dist/vocabulary/index.js +22 -0
  163. package/dist/vocabulary/index.js.map +1 -0
  164. package/dist/vocabulary/keys.d.ts +1 -0
  165. package/dist/vocabulary/keys.js +8 -0
  166. package/dist/vocabulary/keys.js.map +1 -0
  167. package/dist/vocabulary/options.d.ts +7 -0
  168. package/dist/vocabulary/options.js +33 -0
  169. package/dist/vocabulary/options.js.map +1 -0
  170. package/dist/vocabulary/tools.d.ts +7 -0
  171. package/dist/vocabulary/tools.js +99 -0
  172. package/dist/vocabulary/tools.js.map +1 -0
  173. package/dist/vocabulary/types.d.ts +20 -0
  174. package/dist/vocabulary/types.js +3 -0
  175. package/dist/vocabulary/types.js.map +1 -0
  176. package/frontend-vue/app/chat/components/AnswerRecord.vue +95 -0
  177. package/frontend-vue/app/chat/components/ApprovalDock.vue +371 -0
  178. package/frontend-vue/app/chat/components/ChangeSetCard.vue +297 -0
  179. package/frontend-vue/app/chat/components/ChatEmpty.vue +142 -0
  180. package/frontend-vue/app/chat/components/ChatView.vue +664 -406
  181. package/frontend-vue/app/chat/components/Composer.vue +589 -0
  182. package/frontend-vue/app/chat/components/ConnectionBanner.vue +41 -65
  183. package/frontend-vue/app/chat/components/ConversationDrawer.vue +387 -177
  184. package/frontend-vue/app/chat/components/DiffView.vue +131 -0
  185. package/frontend-vue/app/chat/components/FullAutoBanner.vue +76 -0
  186. package/frontend-vue/app/chat/components/FullAutoDialog.vue +103 -0
  187. package/frontend-vue/app/chat/components/MarkdownContent.vue +15 -15
  188. package/frontend-vue/app/chat/components/MessageList.vue +389 -295
  189. package/frontend-vue/app/chat/components/NoticeCard.vue +193 -0
  190. package/frontend-vue/app/chat/components/PanelHeader.vue +242 -0
  191. package/frontend-vue/app/chat/components/PermissionCard.vue +470 -0
  192. package/frontend-vue/app/chat/components/PlanCard.vue +115 -0
  193. package/frontend-vue/app/chat/components/QuestionDock.vue +407 -0
  194. package/frontend-vue/app/chat/components/QueueDock.vue +310 -0
  195. package/frontend-vue/app/chat/components/RulesList.vue +97 -0
  196. package/frontend-vue/app/chat/components/StepsDock.vue +181 -0
  197. package/frontend-vue/app/chat/components/ToolCluster.vue +186 -178
  198. package/frontend-vue/app/chat/components/ToolRow.vue +251 -0
  199. package/frontend-vue/app/chat/components/TurnErrorCard.vue +135 -0
  200. package/frontend-vue/app/chat/composables/conversation-events.ts +404 -0
  201. package/frontend-vue/app/chat/composables/useChangeSetActions.ts +135 -0
  202. package/frontend-vue/app/chat/composables/useChatChannel.ts +11 -3
  203. package/frontend-vue/app/chat/composables/useChatI18n.ts +35 -0
  204. package/frontend-vue/app/chat/composables/useComposerAttachments.ts +111 -0
  205. package/frontend-vue/app/chat/composables/useConversation.ts +149 -651
  206. package/frontend-vue/app/chat/composables/useConversationId.ts +2 -10
  207. package/frontend-vue/app/chat/composables/useConversationList.ts +77 -21
  208. package/frontend-vue/app/chat/composables/useConversationMode.ts +73 -0
  209. package/frontend-vue/app/chat/composables/usePaletteTurn.ts +124 -0
  210. package/frontend-vue/app/chat/composables/usePermissionQueue.ts +211 -34
  211. package/frontend-vue/app/chat/composables/useQuestionQueue.ts +103 -21
  212. package/frontend-vue/app/chat/composables/useQueueActions.ts +55 -0
  213. package/frontend-vue/app/chat/composables/useSettings.ts +17 -34
  214. package/frontend-vue/app/chat/constants/attachments.ts +2 -1
  215. package/frontend-vue/app/chat/constants/conversation.ts +18 -32
  216. package/frontend-vue/app/chat/constants/protocol.ts +25 -2
  217. package/frontend-vue/app/chat/constants/run-status.ts +8 -14
  218. package/frontend-vue/app/chat/constants/settings.ts +5 -47
  219. package/frontend-vue/app/chat/types/conversation.ts +72 -2
  220. package/frontend-vue/app/chat/types/permission.ts +31 -0
  221. package/frontend-vue/app/chat/types/protocol.ts +226 -0
  222. package/frontend-vue/app/chat/types/question.ts +10 -1
  223. package/frontend-vue/app/chat/types/settings.ts +9 -5
  224. package/frontend-vue/app/chat/utils/approval-keyboard.ts +49 -0
  225. package/frontend-vue/app/chat/utils/arrays.ts +10 -0
  226. package/frontend-vue/app/chat/utils/conversation-groups.ts +96 -0
  227. package/frontend-vue/app/chat/utils/conversation-messages.ts +251 -0
  228. package/frontend-vue/app/chat/utils/diff.ts +78 -0
  229. package/frontend-vue/app/chat/utils/format.ts +43 -0
  230. package/frontend-vue/app/chat/utils/markdown.ts +1 -1
  231. package/frontend-vue/app/chat/utils/permission-view.ts +159 -0
  232. package/frontend-vue/app/chat/utils/platform.ts +34 -0
  233. package/frontend-vue/app/chat/utils/queue.ts +18 -0
  234. package/frontend-vue/app/chat/utils/render-items.ts +164 -0
  235. package/frontend-vue/app/chat/utils/run-status.ts +19 -23
  236. package/frontend-vue/app/chat/utils/suggestions.ts +101 -0
  237. package/frontend-vue/app/chat/utils/todos.ts +2 -2
  238. package/frontend-vue/app/chat/utils/tool-lexicon.ts +540 -0
  239. package/frontend-vue/app/chat/utils/tool-state.ts +60 -0
  240. package/frontend-vue/app/components/ActivityDetail.vue +235 -0
  241. package/frontend-vue/app/components/ChangesView.vue +249 -0
  242. package/frontend-vue/app/components/ChatPanel.vue +141 -196
  243. package/frontend-vue/app/components/PaletteAnswer.vue +185 -0
  244. package/frontend-vue/app/components/SkillCard.vue +77 -0
  245. package/frontend-vue/app/components/SkillDetail.vue +188 -0
  246. package/frontend-vue/app/plugins/ai.client.ts +349 -57
  247. package/frontend-vue/app/plugins/side-panel.ts +13 -0
  248. package/frontend-vue/app/runtime/assistant-activity.ts +105 -0
  249. package/frontend-vue/app/runtime/assistant-commands.ts +210 -0
  250. package/frontend-vue/app/runtime/assistant-notifications.ts +181 -0
  251. package/frontend-vue/app/runtime/assistant-session.ts +49 -6
  252. package/frontend-vue/app/runtime/channel-client.ts +3 -6
  253. package/frontend-vue/app/runtime/constants.ts +29 -35
  254. package/frontend-vue/app/runtime/host-commands.ts +4 -4
  255. package/frontend-vue/app/runtime/listeners.ts +1 -1
  256. package/frontend-vue/app/runtime/navigation-complete.ts +1 -1
  257. package/frontend-vue/app/runtime/panel-intents.ts +55 -0
  258. package/frontend-vue/app/runtime/shortcuts.ts +3 -1
  259. package/frontend-vue/app/runtime/sidecar-status.ts +9 -1
  260. package/frontend-vue/app/runtime/stream-holds.ts +24 -0
  261. package/frontend-vue/app/views/ChangeSetPanel.vue +222 -0
  262. package/frontend-vue/app/views/DecisionLabel.vue +30 -0
  263. package/frontend-vue/app/views/DiffView.vue +215 -0
  264. package/frontend-vue/app/views/ResultPill.vue +32 -0
  265. package/frontend-vue/app/views/RowStepper.vue +48 -0
  266. package/frontend-vue/app/views/UnavailableState.vue +66 -0
  267. package/frontend-vue/app/views/composables/useAiRequest.ts +89 -0
  268. package/frontend-vue/app/views/composables/useAssistant.ts +33 -0
  269. package/frontend-vue/app/views/composables/useChangeSetActions.ts +102 -0
  270. package/frontend-vue/app/views/composables/useViewFormat.ts +55 -0
  271. package/frontend-vue/app/views/constants.ts +43 -0
  272. package/frontend-vue/app/views/types.ts +189 -0
  273. package/frontend-vue/app/views/utils/activity.ts +77 -0
  274. package/frontend-vue/app/views/utils/change-sets.ts +44 -0
  275. package/frontend-vue/app/views/utils/diff.ts +98 -0
  276. package/frontend-vue/app/views/utils/moment.ts +92 -0
  277. package/frontend-vue/app/views/utils/skills.ts +27 -0
  278. package/frontend-vue/app/views/utils/tools.ts +73 -0
  279. package/frontend-vue/dms.frontend.ts +17 -5
  280. package/frontend-vue/i18n/locales/dms-ai-pages-en-GB.json +607 -0
  281. package/frontend-vue/i18n/locales/dms-ai-pages-fr-FR.json +607 -0
  282. package/frontend-vue/i18n/locales/dms-ai-panel-en-GB.json +425 -0
  283. package/frontend-vue/i18n/locales/dms-ai-panel-fr-FR.json +425 -0
  284. package/frontend-vue/i18n/locales/dms-ai-views-en-GB.json +142 -0
  285. package/frontend-vue/i18n/locales/dms-ai-views-fr-FR.json +142 -0
  286. package/frontend-vue/package.json +1 -1
  287. package/frontend-vue/tests/chat/conversation-events.test.ts +590 -0
  288. package/frontend-vue/tests/chat/diff-and-keys.test.ts +178 -0
  289. package/frontend-vue/tests/chat/markdown.test.ts +1 -0
  290. package/frontend-vue/tests/chat/tool-lexicon.test.ts +188 -0
  291. package/frontend-vue/tests/chat/useConversation.test.ts +36 -33
  292. package/frontend-vue/tests/chat-panel.test.ts +79 -131
  293. package/frontend-vue/tests/host-commands.test.ts +1 -0
  294. package/frontend-vue/tests/i18n-panel.test.ts +118 -0
  295. package/frontend-vue/tests/navigation.test.ts +1 -0
  296. package/frontend-vue/tests/palette-assistant.test.ts +221 -0
  297. package/frontend-vue/tests/panel-states.test.ts +811 -0
  298. package/frontend-vue/tests/sidecar-guest.test.ts +35 -3
  299. package/frontend-vue/tests/support/i18n.ts +49 -0
  300. package/frontend-vue/tests/support/panel-harness.ts +198 -0
  301. package/frontend-vue/tests/support/stubs.ts +201 -0
  302. package/frontend-vue/tests/views/diff-view.test.ts +150 -0
  303. package/frontend-vue/tests/views/view-format.test.ts +59 -0
  304. package/frontend-vue/vitest.config.ts +4 -1
  305. package/package.json +5 -4
  306. package/sidecar/dist/agent/attachment-files.d.ts +0 -1
  307. package/sidecar/dist/agent/attachment-files.js +1 -1
  308. package/sidecar/dist/agent/attachment-files.js.map +1 -1
  309. package/sidecar/dist/agent/call-ledger.d.ts +6 -0
  310. package/sidecar/dist/agent/call-ledger.js +41 -0
  311. package/sidecar/dist/agent/call-ledger.js.map +1 -0
  312. package/sidecar/dist/agent/command-analysis.d.ts +9 -0
  313. package/sidecar/dist/agent/command-analysis.js +104 -0
  314. package/sidecar/dist/agent/command-analysis.js.map +1 -0
  315. package/sidecar/dist/agent/conversation-modes.d.ts +32 -0
  316. package/sidecar/dist/agent/conversation-modes.js +122 -0
  317. package/sidecar/dist/agent/conversation-modes.js.map +1 -0
  318. package/sidecar/dist/agent/edit-targets.d.ts +5 -0
  319. package/sidecar/dist/agent/edit-targets.js +41 -0
  320. package/sidecar/dist/agent/edit-targets.js.map +1 -0
  321. package/sidecar/dist/agent/effective-mode.d.ts +9 -2
  322. package/sidecar/dist/agent/effective-mode.js +14 -16
  323. package/sidecar/dist/agent/effective-mode.js.map +1 -1
  324. package/sidecar/dist/agent/host-context.js +2 -2
  325. package/sidecar/dist/agent/host-context.js.map +1 -1
  326. package/sidecar/dist/agent/line-diff.d.ts +8 -0
  327. package/sidecar/dist/agent/line-diff.js +204 -0
  328. package/sidecar/dist/agent/line-diff.js.map +1 -0
  329. package/sidecar/dist/agent/permission-bus.d.ts +55 -10
  330. package/sidecar/dist/agent/permission-bus.js +130 -100
  331. package/sidecar/dist/agent/permission-bus.js.map +1 -1
  332. package/sidecar/dist/agent/permission-preview.d.ts +20 -0
  333. package/sidecar/dist/agent/permission-preview.js +203 -0
  334. package/sidecar/dist/agent/permission-preview.js.map +1 -0
  335. package/sidecar/dist/agent/permission-rules.d.ts +17 -0
  336. package/sidecar/dist/agent/permission-rules.js +95 -0
  337. package/sidecar/dist/agent/permission-rules.js.map +1 -0
  338. package/sidecar/dist/agent/provider.d.ts +12 -4
  339. package/sidecar/dist/agent/question-bus.d.ts +14 -2
  340. package/sidecar/dist/agent/question-bus.js +30 -11
  341. package/sidecar/dist/agent/question-bus.js.map +1 -1
  342. package/sidecar/dist/agent/runner-events.d.ts +3 -1
  343. package/sidecar/dist/agent/runner.d.ts +6 -4
  344. package/sidecar/dist/agent/runner.js +24 -7
  345. package/sidecar/dist/agent/runner.js.map +1 -1
  346. package/sidecar/dist/agent/switching-runner.js +1 -0
  347. package/sidecar/dist/agent/switching-runner.js.map +1 -1
  348. package/sidecar/dist/agent/tool-kinds.d.ts +12 -0
  349. package/sidecar/dist/agent/tool-kinds.js +50 -0
  350. package/sidecar/dist/agent/tool-kinds.js.map +1 -0
  351. package/sidecar/dist/agent/typecheck.d.ts +2 -6
  352. package/sidecar/dist/audit/activity-query.d.ts +37 -0
  353. package/sidecar/dist/audit/activity-query.js +111 -0
  354. package/sidecar/dist/audit/activity-query.js.map +1 -0
  355. package/sidecar/dist/audit/activity-target.d.ts +2 -0
  356. package/sidecar/dist/audit/activity-target.js +46 -0
  357. package/sidecar/dist/audit/activity-target.js.map +1 -0
  358. package/sidecar/dist/audit/activity.d.ts +45 -0
  359. package/sidecar/dist/audit/activity.js +255 -0
  360. package/sidecar/dist/audit/activity.js.map +1 -0
  361. package/sidecar/dist/audit/call-audit.d.ts +11 -0
  362. package/sidecar/dist/audit/call-audit.js +23 -0
  363. package/sidecar/dist/audit/call-audit.js.map +1 -0
  364. package/sidecar/dist/checkpoints/checkpoints.d.ts +69 -0
  365. package/sidecar/dist/checkpoints/checkpoints.js +244 -0
  366. package/sidecar/dist/checkpoints/checkpoints.js.map +1 -0
  367. package/sidecar/dist/checkpoints/shadow-git.d.ts +23 -0
  368. package/sidecar/dist/checkpoints/shadow-git.js +173 -0
  369. package/sidecar/dist/checkpoints/shadow-git.js.map +1 -0
  370. package/sidecar/dist/constants/agent.d.ts +2 -0
  371. package/sidecar/dist/constants/agent.js +4 -0
  372. package/sidecar/dist/constants/agent.js.map +1 -1
  373. package/sidecar/dist/constants/audit.d.ts +22 -0
  374. package/sidecar/dist/constants/audit.js +49 -0
  375. package/sidecar/dist/constants/audit.js.map +1 -0
  376. package/sidecar/dist/constants/builder.d.ts +3 -0
  377. package/sidecar/dist/constants/builder.js +2 -0
  378. package/sidecar/dist/constants/builder.js.map +1 -1
  379. package/sidecar/dist/constants/checkpoints.d.ts +15 -0
  380. package/sidecar/dist/constants/checkpoints.js +26 -0
  381. package/sidecar/dist/constants/checkpoints.js.map +1 -0
  382. package/sidecar/dist/constants/claude.d.ts +1 -0
  383. package/sidecar/dist/constants/claude.js +1 -0
  384. package/sidecar/dist/constants/claude.js.map +1 -1
  385. package/sidecar/dist/constants/codex.d.ts +2 -4
  386. package/sidecar/dist/constants/codex.js +2 -4
  387. package/sidecar/dist/constants/codex.js.map +1 -1
  388. package/sidecar/dist/constants/http.d.ts +1 -0
  389. package/sidecar/dist/constants/http.js +1 -0
  390. package/sidecar/dist/constants/http.js.map +1 -1
  391. package/sidecar/dist/constants/mcp.d.ts +3 -0
  392. package/sidecar/dist/constants/mcp.js +9 -0
  393. package/sidecar/dist/constants/mcp.js.map +1 -1
  394. package/sidecar/dist/constants/permissions.d.ts +9 -3
  395. package/sidecar/dist/constants/permissions.js +9 -2
  396. package/sidecar/dist/constants/permissions.js.map +1 -1
  397. package/sidecar/dist/constants/settings.d.ts +2 -1
  398. package/sidecar/dist/constants/settings.js +7 -1
  399. package/sidecar/dist/constants/settings.js.map +1 -1
  400. package/sidecar/dist/constants/tool-kinds.d.ts +14 -0
  401. package/sidecar/dist/constants/tool-kinds.js +65 -0
  402. package/sidecar/dist/constants/tool-kinds.js.map +1 -0
  403. package/sidecar/dist/index.js +28 -2
  404. package/sidecar/dist/index.js.map +1 -1
  405. package/sidecar/dist/mcp/define-tool.d.ts +3 -2
  406. package/sidecar/dist/mcp/tool-definitions.js +4 -1
  407. package/sidecar/dist/mcp/tool-definitions.js.map +1 -1
  408. package/sidecar/dist/mcp/tools/ask-user.d.ts +2 -2
  409. package/sidecar/dist/mcp/tools/ask-user.js +7 -4
  410. package/sidecar/dist/mcp/tools/ask-user.js.map +1 -1
  411. package/sidecar/dist/mcp/tools/builder-catalog.d.ts +3 -0
  412. package/sidecar/dist/mcp/tools/builder-catalog.js +86 -0
  413. package/sidecar/dist/mcp/tools/builder-catalog.js.map +1 -0
  414. package/sidecar/dist/mcp/tools/builder-gate.d.ts +8 -0
  415. package/sidecar/dist/mcp/tools/builder-gate.js +22 -0
  416. package/sidecar/dist/mcp/tools/builder-gate.js.map +1 -0
  417. package/sidecar/dist/mcp/tools/builder.d.ts +7 -2
  418. package/sidecar/dist/mcp/tools/builder.js +48 -7
  419. package/sidecar/dist/mcp/tools/builder.js.map +1 -1
  420. package/sidecar/dist/mcp/types.d.ts +2 -0
  421. package/sidecar/dist/metrics/aggregate.d.ts +13 -10
  422. package/sidecar/dist/metrics/aggregate.js +140 -143
  423. package/sidecar/dist/metrics/aggregate.js.map +1 -1
  424. package/sidecar/dist/metrics/types.d.ts +36 -23
  425. package/sidecar/dist/metrics/types.js +3 -1
  426. package/sidecar/dist/metrics/types.js.map +1 -1
  427. package/sidecar/dist/pages/imports-scanner.d.ts +1 -1
  428. package/sidecar/dist/pages/imports-scanner.js +2 -5
  429. package/sidecar/dist/pages/imports-scanner.js.map +1 -1
  430. package/sidecar/dist/prompts/agent.d.ts +1 -3
  431. package/sidecar/dist/prompts/agent.js +4 -4
  432. package/sidecar/dist/prompts/agent.js.map +1 -1
  433. package/sidecar/dist/prompts/builder.d.ts +2 -2
  434. package/sidecar/dist/prompts/builder.js +2 -2
  435. package/sidecar/dist/protocol/events.d.ts +1084 -18
  436. package/sidecar/dist/protocol/events.js +239 -5
  437. package/sidecar/dist/protocol/events.js.map +1 -1
  438. package/sidecar/dist/protocol/messages.d.ts +273 -19
  439. package/sidecar/dist/protocol/messages.js +107 -7
  440. package/sidecar/dist/protocol/messages.js.map +1 -1
  441. package/sidecar/dist/providers/claude/config.d.ts +3 -2
  442. package/sidecar/dist/providers/claude/config.js +10 -4
  443. package/sidecar/dist/providers/claude/config.js.map +1 -1
  444. package/sidecar/dist/providers/claude/provider.d.ts +2 -8
  445. package/sidecar/dist/providers/claude/provider.js +37 -35
  446. package/sidecar/dist/providers/claude/provider.js.map +1 -1
  447. package/sidecar/dist/providers/claude/safe-mode.d.ts +6 -1
  448. package/sidecar/dist/providers/claude/safe-mode.js +13 -5
  449. package/sidecar/dist/providers/claude/safe-mode.js.map +1 -1
  450. package/sidecar/dist/providers/codex/adapter.d.ts +6 -0
  451. package/sidecar/dist/providers/codex/adapter.js +14 -4
  452. package/sidecar/dist/providers/codex/adapter.js.map +1 -1
  453. package/sidecar/dist/providers/codex/client.d.ts +0 -11
  454. package/sidecar/dist/providers/codex/config.d.ts +1 -3
  455. package/sidecar/dist/providers/codex/config.js +5 -11
  456. package/sidecar/dist/providers/codex/config.js.map +1 -1
  457. package/sidecar/dist/providers/codex/permissions.d.ts +7 -4
  458. package/sidecar/dist/providers/codex/permissions.js +32 -16
  459. package/sidecar/dist/providers/codex/permissions.js.map +1 -1
  460. package/sidecar/dist/providers/codex/process.js +1 -5
  461. package/sidecar/dist/providers/codex/process.js.map +1 -1
  462. package/sidecar/dist/providers/codex/provider.js +14 -3
  463. package/sidecar/dist/providers/codex/provider.js.map +1 -1
  464. package/sidecar/dist/providers/codex/skills.d.ts +0 -1
  465. package/sidecar/dist/providers/codex/skills.js +1 -1
  466. package/sidecar/dist/providers/codex/skills.js.map +1 -1
  467. package/sidecar/dist/providers/codex/stop.js +1 -10
  468. package/sidecar/dist/providers/codex/stop.js.map +1 -1
  469. package/sidecar/dist/server/change-set-actions.d.ts +10 -0
  470. package/sidecar/dist/server/change-set-actions.js +14 -0
  471. package/sidecar/dist/server/change-set-actions.js.map +1 -0
  472. package/sidecar/dist/server/chat-events.d.ts +15 -0
  473. package/sidecar/dist/server/chat-events.js +115 -0
  474. package/sidecar/dist/server/chat-events.js.map +1 -0
  475. package/sidecar/dist/server/chat-socket-registry.d.ts +2 -0
  476. package/sidecar/dist/server/chat-socket-registry.js +48 -37
  477. package/sidecar/dist/server/chat-socket-registry.js.map +1 -1
  478. package/sidecar/dist/server/conversation-runs.d.ts +13 -0
  479. package/sidecar/dist/server/conversation-runs.js +114 -0
  480. package/sidecar/dist/server/conversation-runs.js.map +1 -0
  481. package/sidecar/dist/server/http-io.d.ts +4 -0
  482. package/sidecar/dist/server/http-io.js +31 -0
  483. package/sidecar/dist/server/http-io.js.map +1 -0
  484. package/sidecar/dist/server/http-params.d.ts +9 -0
  485. package/sidecar/dist/server/http-params.js +79 -0
  486. package/sidecar/dist/server/http-params.js.map +1 -0
  487. package/sidecar/dist/server/http-routes.d.ts +27 -0
  488. package/sidecar/dist/server/http-routes.js +281 -0
  489. package/sidecar/dist/server/http-routes.js.map +1 -0
  490. package/sidecar/dist/server/http.d.ts +6 -1
  491. package/sidecar/dist/server/http.js +76 -164
  492. package/sidecar/dist/server/http.js.map +1 -1
  493. package/sidecar/dist/server/pending-queue.d.ts +2 -0
  494. package/sidecar/dist/server/pending-queue.js +16 -0
  495. package/sidecar/dist/server/pending-queue.js.map +1 -1
  496. package/sidecar/dist/server/permission-events.d.ts +15 -0
  497. package/sidecar/dist/server/permission-events.js +163 -0
  498. package/sidecar/dist/server/permission-events.js.map +1 -0
  499. package/sidecar/dist/server/routing.d.ts +4 -52
  500. package/sidecar/dist/server/routing.js +190 -501
  501. package/sidecar/dist/server/routing.js.map +1 -1
  502. package/sidecar/dist/server/safety-net.d.ts +7 -0
  503. package/sidecar/dist/server/safety-net.js +34 -6
  504. package/sidecar/dist/server/safety-net.js.map +1 -1
  505. package/sidecar/dist/server/services.d.ts +55 -0
  506. package/sidecar/dist/server/services.js +2 -0
  507. package/sidecar/dist/server/services.js.map +1 -0
  508. package/sidecar/dist/server/tool-calls.d.ts +6 -0
  509. package/sidecar/dist/server/tool-calls.js +134 -0
  510. package/sidecar/dist/server/tool-calls.js.map +1 -0
  511. package/sidecar/dist/server/turn-registry.d.ts +44 -0
  512. package/sidecar/dist/server/turn-registry.js +40 -0
  513. package/sidecar/dist/server/turn-registry.js.map +1 -0
  514. package/sidecar/dist/server/turns.d.ts +17 -0
  515. package/sidecar/dist/server/turns.js +335 -0
  516. package/sidecar/dist/server/turns.js.map +1 -0
  517. package/sidecar/dist/server/ws.d.ts +4 -1
  518. package/sidecar/dist/server/ws.js +102 -62
  519. package/sidecar/dist/server/ws.js.map +1 -1
  520. package/sidecar/dist/skills/build-catalog.d.ts +1 -0
  521. package/sidecar/dist/skills/build-catalog.js +14 -1
  522. package/sidecar/dist/skills/build-catalog.js.map +1 -1
  523. package/sidecar/dist/skills/skill-usage.d.ts +22 -0
  524. package/sidecar/dist/skills/skill-usage.js +97 -0
  525. package/sidecar/dist/skills/skill-usage.js.map +1 -0
  526. package/sidecar/dist/state/change-sets.d.ts +12 -0
  527. package/sidecar/dist/state/change-sets.js +77 -0
  528. package/sidecar/dist/state/change-sets.js.map +1 -0
  529. package/sidecar/dist/state/conversations.d.ts +19 -7
  530. package/sidecar/dist/state/conversations.js +61 -20
  531. package/sidecar/dist/state/conversations.js.map +1 -1
  532. package/sidecar/dist/state/host-state.d.ts +2 -1
  533. package/sidecar/dist/state/settings-store.d.ts +1 -0
  534. package/sidecar/dist/state/settings-store.js +27 -26
  535. package/sidecar/dist/state/settings-store.js.map +1 -1
  536. package/sidecar/dist/state/settings-types.d.ts +11 -1
  537. package/sidecar/dist/state/settings-types.js +3 -1
  538. package/sidecar/dist/state/settings-types.js.map +1 -1
  539. package/sidecar/dist/state/types.d.ts +114 -8
  540. package/sidecar/dist/state/types.js +6 -3
  541. package/sidecar/dist/state/types.js.map +1 -1
  542. package/sidecar/package.json +2 -1
  543. package/skills/dms-builder-safe/REFERENCE.md +1 -1
  544. package/skills/dms-builder-safe/SKILL.md +12 -7
  545. package/dist/routes/proxy.d.ts +0 -4
  546. package/dist/routes/proxy.js +0 -55
  547. package/dist/routes/proxy.js.map +0 -1
  548. package/frontend-vue/app/chat/components/ComposerInput.vue +0 -742
  549. package/frontend-vue/app/chat/components/PermissionRequests.vue +0 -323
  550. package/frontend-vue/app/chat/components/QuestionPrompt.vue +0 -275
  551. package/frontend-vue/app/chat/components/QueuedMessages.vue +0 -109
  552. package/frontend-vue/app/chat/components/TodoList.vue +0 -291
  553. package/frontend-vue/app/chat/components/ToolCallEntry.vue +0 -218
  554. package/frontend-vue/app/chat/constants/connection-banner.ts +0 -7
  555. package/frontend-vue/app/chat/constants/conversation-drawer.ts +0 -13
  556. package/frontend-vue/app/chat/constants/permissions.ts +0 -50
  557. package/frontend-vue/app/chat/utils/tool-summary.ts +0 -53
  558. package/frontend-vue/app/components/ActivityView.vue +0 -94
  559. package/frontend-vue/app/components/SettingsView.vue +0 -387
  560. package/frontend-vue/app/components/SkillsView.vue +0 -230
  561. package/frontend-vue/app/runtime/header-action.ts +0 -64
  562. package/frontend-vue/app/runtime/overlay-prefs.ts +0 -89
  563. package/frontend-vue/app/runtime/panel-state.ts +0 -88
  564. package/frontend-vue/tests/overlay-prefs.test.ts +0 -91
  565. package/frontend-vue/tests/panel-state.test.ts +0 -58
@@ -1,107 +1,28 @@
1
- import { flushPromises, mount, type VueWrapper } from "@vue/test-utils";
1
+ // @vitest-environment jsdom
2
+ import { flushPromises, mount } from "@vue/test-utils";
2
3
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
- import { nextTick, ref } from "vue";
4
+ import { nextTick } from "vue";
4
5
  import ChatPanel from "../app/components/ChatPanel.vue";
5
6
  import {
6
- ASSISTANT_SESSION_KEY,
7
- type AssistantSession,
8
- } from "../app/runtime/assistant-session";
9
- import type { ChannelStatus } from "../app/runtime/channel-client";
10
- import {
11
- type ChatTransportHub,
12
- createChatTransport,
13
- } from "../app/runtime/chat-transport";
14
- import { resetPrefsForTesting } from "../app/runtime/overlay-prefs";
15
- import { createChatPanelState } from "../app/runtime/panel-state";
16
- import type { SidecarStatus } from "../app/runtime/sidecar-status";
7
+ CONVERSATION_ID,
8
+ createHarness,
9
+ deliver,
10
+ openPanel,
11
+ sendMessage,
12
+ sentOfType,
13
+ unmountPanel,
14
+ providedSession,
15
+ } from "./support/panel-harness";
16
+ import { PANEL_STUBS } from "./support/stubs";
17
17
 
18
- const CONVERSATION_ID = "conv-native-1";
19
18
  const STALL_AFTER_MS = 20_000;
20
19
 
21
- interface Harness {
22
- session: AssistantSession;
23
- hub: ChatTransportHub;
24
- sent: Array<Record<string, unknown>>;
25
- reconnects: number;
26
- sidecarStatus: ReturnType<typeof ref<SidecarStatus>>;
27
- setChannelStatus: (status: ChannelStatus) => void;
28
- navigate: ReturnType<typeof vi.fn>;
29
- }
30
-
31
- function createHarness(): Harness {
32
- let channelStatus: ChannelStatus = "connected";
33
- const harness = {
34
- sent: [] as Array<Record<string, unknown>>,
35
- reconnects: 0,
36
- sidecarStatus: ref<SidecarStatus>("connected"),
37
- navigate: vi.fn(),
38
- } as Harness;
39
- harness.hub = createChatTransport({
40
- send: (msg) => {
41
- if (channelStatus !== "connected") return false;
42
- harness.sent.push(msg as Record<string, unknown>);
43
- return true;
44
- },
45
- reconnect: () => {
46
- harness.reconnects += 1;
47
- },
48
- getStatus: () => channelStatus,
49
- });
50
- harness.setChannelStatus = (status) => {
51
- channelStatus = status;
52
- harness.hub.announceStatus(status);
53
- };
54
- harness.session = {
55
- status: harness.sidecarStatus,
56
- chat: harness.hub.transport,
57
- panel: createChatPanelState(),
58
- navigate: harness.navigate,
59
- };
60
- return harness;
61
- }
62
-
63
- let wrapper: VueWrapper | null = null;
64
-
65
- function mountPanel(harness: Harness): VueWrapper {
66
- wrapper = mount(ChatPanel, {
67
- attachTo: document.body,
68
- global: {
69
- provide: { [ASSISTANT_SESSION_KEY as symbol]: harness.session },
70
- stubs: { UIcon: true, USelect: true },
71
- },
72
- });
73
- return wrapper;
74
- }
75
-
76
- async function openPanel(harness: Harness): Promise<VueWrapper> {
77
- const mounted = mountPanel(harness);
78
- harness.session.panel.toggle();
79
- await nextTick();
80
- await flushPromises();
81
- return mounted;
82
- }
83
-
84
- function deliver(harness: Harness, event: Record<string, unknown>): void {
85
- harness.hub.deliver({ conversationId: CONVERSATION_ID, ...event });
86
- }
87
-
88
- async function sendMessage(panel: VueWrapper, text: string): Promise<void> {
89
- await panel.find("textarea").setValue(text);
90
- await panel.find("form.composer").trigger("submit");
91
- }
92
-
93
- function sentOfType(harness: Harness, type: string) {
94
- return harness.sent.filter((msg) => msg.type === type);
95
- }
96
-
97
20
  beforeEach(() => {
98
21
  localStorage.setItem("dms-ai-conversation-id", CONVERSATION_ID);
99
22
  });
100
23
 
101
24
  afterEach(() => {
102
- wrapper?.unmount();
103
- wrapper = null;
104
- resetPrefsForTesting();
25
+ unmountPanel();
105
26
  localStorage.clear();
106
27
  vi.useRealTimers();
107
28
  });
@@ -111,7 +32,9 @@ describe("the chat as a component of the dashboard", () => {
111
32
  const harness = createHarness();
112
33
  const panel = await openPanel(harness);
113
34
  expect(document.querySelector("iframe")).toBeNull();
114
- expect(document.getElementById("dms-ai-overlay-root")).not.toBeNull();
35
+ expect(panel.find("[data-dms-side-panel] .dms-ai-panel").exists()).toBe(
36
+ true,
37
+ );
115
38
  expect(panel.find(".chat-view").exists()).toBe(true);
116
39
  expect(Reflect.has(globalThis, "dmsAiChatTransport")).toBe(false);
117
40
  });
@@ -124,13 +47,26 @@ describe("the chat as a component of the dashboard", () => {
124
47
  ]);
125
48
  });
126
49
 
127
- it("keeps its conversation when the panel is closed and opened again", async () => {
50
+ it("leaves its conversation when the panel closes, and picks it up again when it opens", async () => {
128
51
  const harness = createHarness();
129
52
  const panel = await openPanel(harness);
130
53
  await sendMessage(panel, "build me a page");
131
54
  harness.session.panel.close();
132
55
  await nextTick();
56
+ expect(panel.find(".chat-view").exists()).toBe(false);
57
+ expect(sentOfType(harness, "leave_conversation")).toEqual([
58
+ { type: "leave_conversation", conversationId: CONVERSATION_ID },
59
+ ]);
133
60
  harness.session.panel.toggle();
61
+ await flushPromises();
62
+ expect(sentOfType(harness, "hello")).toEqual([
63
+ { type: "hello", role: "chat", conversationId: CONVERSATION_ID },
64
+ { type: "hello", role: "chat", conversationId: CONVERSATION_ID },
65
+ ]);
66
+ deliver(harness, {
67
+ type: "conversation_snapshot",
68
+ messages: [{ role: "user", content: "build me a page", timestampMs: 1 }],
69
+ });
134
70
  await nextTick();
135
71
  expect(panel.text()).toContain("build me a page");
136
72
  });
@@ -138,61 +74,68 @@ describe("the chat as a component of the dashboard", () => {
138
74
  it("opens the settings page through the dashboard router, not the sidecar", async () => {
139
75
  const harness = createHarness();
140
76
  const panel = await openPanel(harness);
141
- await panel.find('button[aria-label="Settings"]').trigger("click");
77
+ const settings = panel
78
+ .findAll(".u-dropdown-item")
79
+ .find((item) => item.text() === "Settings");
80
+ await settings?.trigger("click");
142
81
  expect(harness.navigate).toHaveBeenCalledExactlyOnceWith(
143
82
  "/modules/ai/settings",
144
83
  );
145
84
  expect(sentOfType(harness, "request_host_navigate")).toEqual([]);
146
85
  });
147
86
 
148
- it("closes from its own button, and on a click elsewhere in the dashboard", async () => {
87
+ it("asks for the conversation list again once a stream that was down connects", async () => {
88
+ const harness = createHarness();
89
+ harness.setChannelStatus("connecting");
90
+ await openPanel(harness);
91
+ expect(sentOfType(harness, "list_conversations")).toEqual([]);
92
+ harness.setChannelStatus("connected");
93
+ await nextTick();
94
+ expect(sentOfType(harness, "list_conversations")).toHaveLength(1);
95
+ });
96
+
97
+ it("closes from its own button, and stays open on a click elsewhere: docked, it is part of the page", async () => {
149
98
  const harness = createHarness();
150
99
  const panel = await openPanel(harness);
151
- panel
152
- .find(".chat-view")
153
- .element.dispatchEvent(
154
- new PointerEvent("pointerdown", { bubbles: true, composed: true }),
155
- );
156
- expect(harness.session.panel.isOpen.value).toBe(true);
157
100
  document.body.dispatchEvent(
158
101
  new PointerEvent("pointerdown", { bubbles: true, composed: true }),
159
102
  );
160
- expect(harness.session.panel.isOpen.value).toBe(false);
161
- harness.session.panel.toggle();
162
- await nextTick();
103
+ expect(harness.session.panel.isOpen.value).toBe(true);
163
104
  await panel.find('button[aria-label="Close"]').trigger("click");
164
105
  expect(harness.session.panel.isOpen.value).toBe(false);
165
106
  });
166
107
 
167
- it("stays open on a click in what the dashboard portals out of the app", async () => {
168
- const harness = createHarness();
169
- await openPanel(harness);
170
- const overlays = document.createElement("div");
171
- overlays.id = "dms-overlays";
172
- const toast = document.createElement("button");
173
- overlays.append(toast);
174
- document.body.append(overlays);
175
- toast.dispatchEvent(
176
- new PointerEvent("pointerdown", { bubbles: true, composed: true }),
108
+ it("shows the first connection while the session is not there yet", async () => {
109
+ const panel = mount(ChatPanel, {
110
+ global: { provide: providedSession(null), stubs: PANEL_STUBS },
111
+ });
112
+ await nextTick();
113
+ expect(panel.find(".dms-ai-panel-status").text()).toContain(
114
+ "Connecting the assistant",
177
115
  );
178
- expect(harness.session.panel.isOpen.value).toBe(true);
179
- overlays.remove();
116
+ expect(panel.find(".chat-view").exists()).toBe(false);
117
+ panel.unmount();
180
118
  });
181
119
 
182
- it("covers the chat with the sidecar status while it is not reachable, and can still be closed", async () => {
120
+ it("keeps the transcript under a banner while the sidecar revives, and offers a restart once it gave up", async () => {
183
121
  const harness = createHarness();
122
+ harness.lastError.value = "EADDRINUSE: port 5010 is already in use";
184
123
  const panel = await openPanel(harness);
185
124
  harness.sidecarStatus.value = "reviving";
186
125
  await nextTick();
187
- expect(panel.find(".dms-ai-panel-status").text()).toContain(
188
- "Reconnecting the assistant",
189
- );
126
+ expect(panel.find(".dms-ai-panel-status").exists()).toBe(false);
127
+ expect(panel.find(".connection-banner").text()).toContain("restarting");
190
128
  harness.sidecarStatus.value = "unavailable";
191
129
  await nextTick();
192
- expect(panel.find(".dms-ai-panel-status").text()).toContain(
193
- "Assistant unavailable",
194
- );
195
- await panel.find(".dms-ai-panel-status button").trigger("click");
130
+ const screen = panel.find(".dms-ai-panel-status");
131
+ expect(screen.text()).toContain("The assistant isn't running");
132
+ expect(screen.text()).toContain("EADDRINUSE");
133
+ const restart = screen
134
+ .findAll("button")
135
+ .find((button) => button.text() === "Restart assistant");
136
+ await restart?.trigger("click");
137
+ expect(harness.restart).toHaveBeenCalledOnce();
138
+ await screen.find('button[aria-label="Close"]').trigger("click");
196
139
  expect(harness.session.panel.isOpen.value).toBe(false);
197
140
  harness.sidecarStatus.value = "connected";
198
141
  await nextTick();
@@ -271,7 +214,7 @@ describe("a running turn shows what it is doing, and never spins forever", () =>
271
214
  expect(panel.find(".thinking").exists()).toBe(true);
272
215
  });
273
216
 
274
- it("shows a run error with Retry, which sends the message again", async () => {
217
+ it("shows a run error with Retry turn, which asks the sidecar to run it again", async () => {
275
218
  const harness = createHarness();
276
219
  const panel = await openPanel(harness);
277
220
  await sendMessage(panel, "reproduce this page");
@@ -279,8 +222,11 @@ describe("a running turn shows what it is doing, and never spins forever", () =>
279
222
  await nextTick();
280
223
  const error = panel.find(".message-bubble-error");
281
224
  expect(error.text()).toContain("API Error: 529 overloaded");
282
- await error.find("button").trigger("click");
283
- expect(sentOfType(harness, "user_message")).toHaveLength(2);
225
+ const retry = error
226
+ .findAll("button")
227
+ .find((button) => button.text() === "Retry turn");
228
+ await retry?.trigger("click");
229
+ expect(sentOfType(harness, "retry_turn")).toHaveLength(1);
284
230
  });
285
231
 
286
232
  it("offers no Retry for a run error that sending again cannot fix", async () => {
@@ -295,7 +241,7 @@ describe("a running turn shows what it is doing, and never spins forever", () =>
295
241
  await nextTick();
296
242
  const error = panel.find(".message-bubble-error");
297
243
  expect(error.text()).toContain("Prompt is too long");
298
- expect(error.find("button").exists()).toBe(false);
244
+ expect(error.text()).not.toContain("Retry turn");
299
245
  });
300
246
 
301
247
  it("says a message was not sent when the stream dropped as it left", async () => {
@@ -333,7 +279,9 @@ describe("the shared stream going away and coming back", () => {
333
279
  const panel = await openPanel(harness);
334
280
  harness.setChannelStatus("reconnecting");
335
281
  await nextTick();
336
- expect(panel.find(".connection-banner").text()).toContain("Reconnecting");
282
+ expect(panel.find(".connection-banner").text()).toContain(
283
+ "Connection lost",
284
+ );
337
285
  expect(panel.find("textarea").attributes("disabled")).toBeDefined();
338
286
  harness.setChannelStatus("connected");
339
287
  harness.hub.announceReady();
@@ -1,3 +1,4 @@
1
+ // @vitest-environment jsdom
1
2
  import { describe, expect, it, vi } from "vitest";
2
3
  import {
3
4
  createHostCommandDispatcher,
@@ -0,0 +1,118 @@
1
+ import { readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { describe, expect, it } from "vitest";
4
+ import { TOOL_VERB_KEYS } from "../app/chat/utils/tool-lexicon";
5
+ import en from "../i18n/locales/dms-ai-panel-en-GB.json";
6
+ import fr from "../i18n/locales/dms-ai-panel-fr-FR.json";
7
+
8
+ type Tree = Record<string, unknown>;
9
+
10
+ const ROOT = resolve(__dirname, "..");
11
+ const SOURCES = [
12
+ "app/chat",
13
+ "app/runtime",
14
+ "app/plugins",
15
+ "app/components/ChatPanel.vue",
16
+ ];
17
+ const LITERAL_KEY =
18
+ /["'`](dms_ai\.(?:panel|tools|common)\.[A-Za-z0-9_.]+[A-Za-z0-9_])["'`]/g;
19
+ const OWNED_NAMESPACES = ["panel", "tools", "common"];
20
+
21
+ /** Keys built at run time from a value, each with every value it can take. */
22
+ const DYNAMIC_FAMILIES: Record<string, readonly string[]> = {
23
+ "dms_ai.panel.tool.state": [
24
+ "done",
25
+ "running",
26
+ "waiting",
27
+ "denied",
28
+ "blocked",
29
+ "failed",
30
+ "stopped",
31
+ ],
32
+ "dms_ai.panel.change.status": ["added", "modified", "deleted"],
33
+ "dms_ai.panel.change.typecheck": ["passed", "failed", "skipped"],
34
+ "dms_ai.common.scope": ["safe", "vibe"],
35
+ "dms_ai.common.agent": ["claude", "codex"],
36
+ "dms_ai.panel.composer.scope_desc": ["safe", "vibe"],
37
+ "dms_ai.panel.full_auto": [
38
+ "duration_turn",
39
+ "duration_30m",
40
+ "duration_chat",
41
+ "banner_turn",
42
+ "banner_30m",
43
+ "banner_chat",
44
+ ],
45
+ "dms_ai.panel.drawer": [
46
+ "group_active",
47
+ "group_today",
48
+ "group_week",
49
+ "group_older",
50
+ ],
51
+ "dms_ai.panel.empty": ["text_safe", "text_vibe"],
52
+ "dms_ai.panel.approvals": [
53
+ "why_deletes_data",
54
+ "why_removes_code",
55
+ "effect_adds_dependency",
56
+ "effect_removes_dependency",
57
+ ],
58
+ "dms_ai.panel.rules": [
59
+ "kind_file",
60
+ "kind_directory",
61
+ "kind_command",
62
+ "kind_domain",
63
+ ],
64
+ };
65
+
66
+ function files(path: string): string[] {
67
+ const full = join(ROOT, path);
68
+ if (!statSync(full).isDirectory()) return [full];
69
+ return readdirSync(full).flatMap((entry) => files(join(path, entry)));
70
+ }
71
+
72
+ function flatten(tree: Tree, prefix = ""): string[] {
73
+ return Object.entries(tree).flatMap(([key, value]) =>
74
+ value !== null && typeof value === "object"
75
+ ? flatten(value as Tree, `${prefix}${key}.`)
76
+ : [`${prefix}${key}`],
77
+ );
78
+ }
79
+
80
+ function usedKeys(): string[] {
81
+ const source = SOURCES.flatMap(files)
82
+ .map((file) => readFileSync(file, "utf8"))
83
+ .join("\n");
84
+ return [
85
+ ...new Set([...source.matchAll(LITERAL_KEY)].map((match) => match[1])),
86
+ ];
87
+ }
88
+
89
+ const dynamicKeys = Object.entries(DYNAMIC_FAMILIES).flatMap(
90
+ ([prefix, values]) => values.map((value) => `${prefix}.${value}`),
91
+ );
92
+
93
+ describe("the panel's strings", () => {
94
+ const english = new Set(flatten(en));
95
+ const french = new Set(flatten(fr));
96
+
97
+ it("exist in English and French alike", () => {
98
+ expect([...english].sort()).toEqual([...french].sort());
99
+ });
100
+
101
+ it("cover every key the panel uses", () => {
102
+ const keys = [
103
+ ...usedKeys(),
104
+ ...dynamicKeys,
105
+ ...TOOL_VERB_KEYS.map((verb) => `dms_ai.tools.${verb}`),
106
+ ];
107
+ expect(keys.filter((key) => !english.has(key))).toEqual([]);
108
+ });
109
+
110
+ it("stay in the panel's own namespaces", () => {
111
+ expect(Object.keys(en.dms_ai).sort()).toEqual([...OWNED_NAMESPACES].sort());
112
+ });
113
+
114
+ it("hold no character vue-i18n would read as syntax", () => {
115
+ const texts = [JSON.stringify(en), JSON.stringify(fr)];
116
+ for (const text of texts) expect(text).not.toMatch(/[@$]/);
117
+ });
118
+ });
@@ -1,3 +1,4 @@
1
+ // @vitest-environment jsdom
1
2
  import { describe, expect, it, vi } from "vitest";
2
3
  import { installCurrentPageTracker } from "../app/runtime/current-page";
3
4
  import { installNavigationCompleteEmitter } from "../app/runtime/navigation-complete";
@@ -0,0 +1,221 @@
1
+ // @vitest-environment jsdom
2
+ import { flushPromises, mount, type VueWrapper } from "@vue/test-utils";
3
+ import { afterEach, describe, expect, it, vi } from "vitest";
4
+ import { nextTick, ref } from "vue";
5
+ import PaletteAnswer from "../app/components/PaletteAnswer.vue";
6
+ import type { CurrentPage } from "../app/chat/types/conversation";
7
+ import {
8
+ assistantSidePanel,
9
+ launcherAction,
10
+ paletteAssistant,
11
+ } from "../app/runtime/assistant-commands";
12
+ import {
13
+ createHarness,
14
+ type Harness,
15
+ providedSession,
16
+ sentOfType,
17
+ } from "./support/panel-harness";
18
+ import { createTestI18n } from "./support/i18n";
19
+ import { PANEL_STUBS } from "./support/stubs";
20
+
21
+ const PROMPT = "What does this page show?";
22
+
23
+ let answer: VueWrapper | null = null;
24
+
25
+ afterEach(() => {
26
+ answer?.unmount();
27
+ answer = null;
28
+ localStorage.clear();
29
+ });
30
+
31
+ function askFromPalette(harness: Harness, close = vi.fn()): VueWrapper {
32
+ answer = mount(PaletteAnswer, {
33
+ props: { prompt: PROMPT, close },
34
+ global: { provide: providedSession(harness.session), stubs: PANEL_STUBS },
35
+ });
36
+ return answer;
37
+ }
38
+
39
+ function paletteConversation(harness: Harness): string {
40
+ const hello = sentOfType(harness, "hello")[0];
41
+ return String(hello?.conversationId);
42
+ }
43
+
44
+ function deliverToPalette(
45
+ harness: Harness,
46
+ event: Record<string, unknown>,
47
+ ): void {
48
+ harness.hub.deliver({
49
+ conversationId: paletteConversation(harness),
50
+ ...event,
51
+ });
52
+ }
53
+
54
+ describe("the assistant's registrations in the dashboard", () => {
55
+ it("docks the panel by id, and lets the DMS toggle it from the launcher", () => {
56
+ const { t } = createTestI18n();
57
+ const panel = assistantSidePanel();
58
+ expect(panel).toMatchObject({
59
+ id: "dms-ai:assistant",
60
+ component: "DmsAiChatPanel",
61
+ ariaLabel: "$dms_ai.panel.launcher",
62
+ });
63
+ expect(panel.minWidth).toBeLessThan(panel.defaultWidth);
64
+ expect(panel.defaultWidth).toBeLessThan(panel.maxWidth);
65
+ const launcher = launcherAction(t);
66
+ expect(launcher.sidePanelId).toBe(panel.id);
67
+ expect(launcher).not.toHaveProperty("onSelect");
68
+ expect(launcher).not.toHaveProperty("isActive");
69
+ });
70
+
71
+ it("offers the palette prompts that fit the page on screen", () => {
72
+ const { t } = createTestI18n();
73
+ const page = ref<CurrentPage | null>({
74
+ path: "/sales/overview",
75
+ title: "Sales overview · DMS",
76
+ });
77
+ const assistant = paletteAssistant(t, page);
78
+ expect(assistant).toMatchObject({
79
+ id: "dms-ai:assistant",
80
+ answerComponent: "DmsAiPaletteAnswer",
81
+ label: "$dms_ai.panel.palette.assistant",
82
+ placeholder: "$dms_ai.panel.palette.placeholder",
83
+ });
84
+ expect(assistant.suggestions().map((item) => item.label)).toEqual([
85
+ "Explain what Sales overview shows",
86
+ "Add a chart to Sales overview",
87
+ "Add a filter to the table on Sales overview",
88
+ ]);
89
+ page.value = { path: "/home", title: "Home" };
90
+ expect(assistant.suggestions().map((item) => item.label)).toContain(
91
+ "Explain how this project is organised",
92
+ );
93
+ });
94
+ });
95
+
96
+ describe("an answer in the command palette", () => {
97
+ it("asks read-only, in a conversation of its own it follows next to the panel's", async () => {
98
+ const harness = createHarness();
99
+ askFromPalette(harness);
100
+ await flushPromises();
101
+ const conversationId = paletteConversation(harness);
102
+ expect(sentOfType(harness, "hello")).toEqual([
103
+ { type: "hello", role: "chat", conversationId, follow: true },
104
+ ]);
105
+ expect(sentOfType(harness, "set_conversation_mode")).toEqual([
106
+ {
107
+ type: "set_conversation_mode",
108
+ conversationId,
109
+ mode: "plan",
110
+ generationMode: "safe",
111
+ },
112
+ ]);
113
+ expect(sentOfType(harness, "user_message")).toMatchObject([
114
+ { conversationId, content: PROMPT },
115
+ ]);
116
+ expect(harness.streamHolds).toBe(1);
117
+ });
118
+
119
+ it("waits for the stream before it asks", async () => {
120
+ const harness = createHarness();
121
+ harness.setChannelStatus("connecting");
122
+ askFromPalette(harness);
123
+ await flushPromises();
124
+ expect(sentOfType(harness, "user_message")).toEqual([]);
125
+ harness.setChannelStatus("connected");
126
+ harness.hub.announceReady();
127
+ await flushPromises();
128
+ expect(sentOfType(harness, "user_message")).toHaveLength(1);
129
+ expect(sentOfType(harness, "hello")[0]).toMatchObject({ follow: true });
130
+ });
131
+
132
+ it("streams the answer, and ignores the panel's conversation", async () => {
133
+ const harness = createHarness();
134
+ const view = askFromPalette(harness);
135
+ await flushPromises();
136
+ harness.hub.deliver({
137
+ conversationId: "conv-of-the-panel",
138
+ type: "assistant_message_chunk",
139
+ text: "not for the palette",
140
+ });
141
+ deliverToPalette(harness, {
142
+ type: "assistant_message_chunk",
143
+ text: "It lists the **sales** of the quarter.",
144
+ });
145
+ deliverToPalette(harness, { type: "run_done" });
146
+ await flushPromises();
147
+ expect(view.text()).toContain("It lists the sales of the quarter.");
148
+ expect(view.text()).not.toContain("not for the palette");
149
+ expect(view.text()).not.toContain(PROMPT);
150
+ });
151
+
152
+ it("continues in the assistant: the panel opens on that conversation and the turn keeps running", async () => {
153
+ const harness = createHarness();
154
+ const close = vi.fn();
155
+ const view = askFromPalette(harness, close);
156
+ await flushPromises();
157
+ const conversationId = paletteConversation(harness);
158
+ const button = view
159
+ .findAll("button")
160
+ .find((item) => item.text() === "Continue in the assistant");
161
+ await button?.trigger("click");
162
+ expect(close).toHaveBeenCalledOnce();
163
+ expect(harness.session.panel.isOpen.value).toBe(true);
164
+ expect(harness.session.intents.pending.value).toEqual({
165
+ kind: "open",
166
+ conversationId,
167
+ });
168
+ view.unmount();
169
+ answer = null;
170
+ expect(sentOfType(harness, "interrupt_turn")).toEqual([]);
171
+ expect(harness.streamHolds).toBe(0);
172
+ });
173
+
174
+ it("stops its turn when the palette closes on it, and lets the stream go", async () => {
175
+ const harness = createHarness();
176
+ const view = askFromPalette(harness);
177
+ await flushPromises();
178
+ const conversationId = paletteConversation(harness);
179
+ view.unmount();
180
+ answer = null;
181
+ expect(sentOfType(harness, "interrupt_turn")).toEqual([
182
+ { type: "interrupt_turn", conversationId },
183
+ ]);
184
+ expect(harness.streamHolds).toBe(0);
185
+ });
186
+
187
+ it("says when the agent waits for the user, who answers in the assistant", async () => {
188
+ const harness = createHarness();
189
+ const view = askFromPalette(harness);
190
+ await flushPromises();
191
+ deliverToPalette(harness, {
192
+ type: "ask_question",
193
+ requestId: "q1",
194
+ questions: [
195
+ {
196
+ header: "Scope",
197
+ question: "Which table?",
198
+ options: [{ label: "Orders" }, { label: "Customers" }],
199
+ multiSelect: false,
200
+ },
201
+ ],
202
+ createdAtMs: Date.now(),
203
+ expiresAtMs: Date.now() + 60_000,
204
+ });
205
+ await nextTick();
206
+ expect(view.find('[role="alert"]').text()).toContain(
207
+ "The assistant is waiting for you",
208
+ );
209
+ });
210
+
211
+ it("says the assistant does not run in this tab without a session", () => {
212
+ answer = mount(PaletteAnswer, {
213
+ props: { prompt: PROMPT, close: vi.fn() },
214
+ global: { provide: providedSession(null), stubs: PANEL_STUBS },
215
+ });
216
+ expect(answer.text()).toContain("isn't running in this tab");
217
+ expect(answer.findAll("button").map((button) => button.text())).toEqual([
218
+ "Close",
219
+ ]);
220
+ });
221
+ });