@antelopejs/dms-ai 0.1.6 → 0.2.0

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 (560) hide show
  1. package/README.md +48 -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 +122 -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/tool-lexicon.test.ts +188 -0
  290. package/frontend-vue/tests/chat/useConversation.test.ts +36 -33
  291. package/frontend-vue/tests/chat-panel.test.ts +78 -131
  292. package/frontend-vue/tests/i18n-panel.test.ts +118 -0
  293. package/frontend-vue/tests/palette-assistant.test.ts +220 -0
  294. package/frontend-vue/tests/panel-states.test.ts +806 -0
  295. package/frontend-vue/tests/sidecar-guest.test.ts +35 -3
  296. package/frontend-vue/tests/support/i18n.ts +49 -0
  297. package/frontend-vue/tests/support/panel-harness.ts +198 -0
  298. package/frontend-vue/tests/support/stubs.ts +201 -0
  299. package/frontend-vue/tests/views/diff-view.test.ts +149 -0
  300. package/frontend-vue/tests/views/view-format.test.ts +59 -0
  301. package/package.json +4 -3
  302. package/sidecar/dist/agent/attachment-files.d.ts +0 -1
  303. package/sidecar/dist/agent/attachment-files.js +1 -1
  304. package/sidecar/dist/agent/attachment-files.js.map +1 -1
  305. package/sidecar/dist/agent/call-ledger.d.ts +6 -0
  306. package/sidecar/dist/agent/call-ledger.js +41 -0
  307. package/sidecar/dist/agent/call-ledger.js.map +1 -0
  308. package/sidecar/dist/agent/command-analysis.d.ts +9 -0
  309. package/sidecar/dist/agent/command-analysis.js +104 -0
  310. package/sidecar/dist/agent/command-analysis.js.map +1 -0
  311. package/sidecar/dist/agent/conversation-modes.d.ts +32 -0
  312. package/sidecar/dist/agent/conversation-modes.js +122 -0
  313. package/sidecar/dist/agent/conversation-modes.js.map +1 -0
  314. package/sidecar/dist/agent/edit-targets.d.ts +5 -0
  315. package/sidecar/dist/agent/edit-targets.js +41 -0
  316. package/sidecar/dist/agent/edit-targets.js.map +1 -0
  317. package/sidecar/dist/agent/effective-mode.d.ts +9 -2
  318. package/sidecar/dist/agent/effective-mode.js +14 -16
  319. package/sidecar/dist/agent/effective-mode.js.map +1 -1
  320. package/sidecar/dist/agent/host-context.js +2 -2
  321. package/sidecar/dist/agent/host-context.js.map +1 -1
  322. package/sidecar/dist/agent/line-diff.d.ts +8 -0
  323. package/sidecar/dist/agent/line-diff.js +204 -0
  324. package/sidecar/dist/agent/line-diff.js.map +1 -0
  325. package/sidecar/dist/agent/permission-bus.d.ts +55 -10
  326. package/sidecar/dist/agent/permission-bus.js +130 -100
  327. package/sidecar/dist/agent/permission-bus.js.map +1 -1
  328. package/sidecar/dist/agent/permission-preview.d.ts +20 -0
  329. package/sidecar/dist/agent/permission-preview.js +203 -0
  330. package/sidecar/dist/agent/permission-preview.js.map +1 -0
  331. package/sidecar/dist/agent/permission-rules.d.ts +17 -0
  332. package/sidecar/dist/agent/permission-rules.js +95 -0
  333. package/sidecar/dist/agent/permission-rules.js.map +1 -0
  334. package/sidecar/dist/agent/provider.d.ts +12 -4
  335. package/sidecar/dist/agent/question-bus.d.ts +14 -2
  336. package/sidecar/dist/agent/question-bus.js +30 -11
  337. package/sidecar/dist/agent/question-bus.js.map +1 -1
  338. package/sidecar/dist/agent/runner-events.d.ts +3 -1
  339. package/sidecar/dist/agent/runner.d.ts +6 -4
  340. package/sidecar/dist/agent/runner.js +24 -7
  341. package/sidecar/dist/agent/runner.js.map +1 -1
  342. package/sidecar/dist/agent/switching-runner.js +1 -0
  343. package/sidecar/dist/agent/switching-runner.js.map +1 -1
  344. package/sidecar/dist/agent/tool-kinds.d.ts +12 -0
  345. package/sidecar/dist/agent/tool-kinds.js +50 -0
  346. package/sidecar/dist/agent/tool-kinds.js.map +1 -0
  347. package/sidecar/dist/agent/typecheck.d.ts +2 -6
  348. package/sidecar/dist/audit/activity-query.d.ts +37 -0
  349. package/sidecar/dist/audit/activity-query.js +111 -0
  350. package/sidecar/dist/audit/activity-query.js.map +1 -0
  351. package/sidecar/dist/audit/activity-target.d.ts +2 -0
  352. package/sidecar/dist/audit/activity-target.js +46 -0
  353. package/sidecar/dist/audit/activity-target.js.map +1 -0
  354. package/sidecar/dist/audit/activity.d.ts +45 -0
  355. package/sidecar/dist/audit/activity.js +255 -0
  356. package/sidecar/dist/audit/activity.js.map +1 -0
  357. package/sidecar/dist/audit/call-audit.d.ts +11 -0
  358. package/sidecar/dist/audit/call-audit.js +23 -0
  359. package/sidecar/dist/audit/call-audit.js.map +1 -0
  360. package/sidecar/dist/checkpoints/checkpoints.d.ts +69 -0
  361. package/sidecar/dist/checkpoints/checkpoints.js +244 -0
  362. package/sidecar/dist/checkpoints/checkpoints.js.map +1 -0
  363. package/sidecar/dist/checkpoints/shadow-git.d.ts +23 -0
  364. package/sidecar/dist/checkpoints/shadow-git.js +173 -0
  365. package/sidecar/dist/checkpoints/shadow-git.js.map +1 -0
  366. package/sidecar/dist/constants/agent.d.ts +2 -0
  367. package/sidecar/dist/constants/agent.js +4 -0
  368. package/sidecar/dist/constants/agent.js.map +1 -1
  369. package/sidecar/dist/constants/audit.d.ts +22 -0
  370. package/sidecar/dist/constants/audit.js +49 -0
  371. package/sidecar/dist/constants/audit.js.map +1 -0
  372. package/sidecar/dist/constants/builder.d.ts +3 -0
  373. package/sidecar/dist/constants/builder.js +2 -0
  374. package/sidecar/dist/constants/builder.js.map +1 -1
  375. package/sidecar/dist/constants/checkpoints.d.ts +15 -0
  376. package/sidecar/dist/constants/checkpoints.js +26 -0
  377. package/sidecar/dist/constants/checkpoints.js.map +1 -0
  378. package/sidecar/dist/constants/claude.d.ts +1 -0
  379. package/sidecar/dist/constants/claude.js +1 -0
  380. package/sidecar/dist/constants/claude.js.map +1 -1
  381. package/sidecar/dist/constants/codex.d.ts +2 -4
  382. package/sidecar/dist/constants/codex.js +2 -4
  383. package/sidecar/dist/constants/codex.js.map +1 -1
  384. package/sidecar/dist/constants/http.d.ts +1 -0
  385. package/sidecar/dist/constants/http.js +1 -0
  386. package/sidecar/dist/constants/http.js.map +1 -1
  387. package/sidecar/dist/constants/mcp.d.ts +3 -0
  388. package/sidecar/dist/constants/mcp.js +9 -0
  389. package/sidecar/dist/constants/mcp.js.map +1 -1
  390. package/sidecar/dist/constants/permissions.d.ts +9 -3
  391. package/sidecar/dist/constants/permissions.js +9 -2
  392. package/sidecar/dist/constants/permissions.js.map +1 -1
  393. package/sidecar/dist/constants/settings.d.ts +2 -1
  394. package/sidecar/dist/constants/settings.js +7 -1
  395. package/sidecar/dist/constants/settings.js.map +1 -1
  396. package/sidecar/dist/constants/tool-kinds.d.ts +14 -0
  397. package/sidecar/dist/constants/tool-kinds.js +65 -0
  398. package/sidecar/dist/constants/tool-kinds.js.map +1 -0
  399. package/sidecar/dist/index.js +28 -2
  400. package/sidecar/dist/index.js.map +1 -1
  401. package/sidecar/dist/mcp/define-tool.d.ts +3 -2
  402. package/sidecar/dist/mcp/tool-definitions.js +4 -1
  403. package/sidecar/dist/mcp/tool-definitions.js.map +1 -1
  404. package/sidecar/dist/mcp/tools/ask-user.d.ts +2 -2
  405. package/sidecar/dist/mcp/tools/ask-user.js +7 -4
  406. package/sidecar/dist/mcp/tools/ask-user.js.map +1 -1
  407. package/sidecar/dist/mcp/tools/builder-catalog.d.ts +3 -0
  408. package/sidecar/dist/mcp/tools/builder-catalog.js +86 -0
  409. package/sidecar/dist/mcp/tools/builder-catalog.js.map +1 -0
  410. package/sidecar/dist/mcp/tools/builder-gate.d.ts +8 -0
  411. package/sidecar/dist/mcp/tools/builder-gate.js +22 -0
  412. package/sidecar/dist/mcp/tools/builder-gate.js.map +1 -0
  413. package/sidecar/dist/mcp/tools/builder.d.ts +7 -2
  414. package/sidecar/dist/mcp/tools/builder.js +48 -7
  415. package/sidecar/dist/mcp/tools/builder.js.map +1 -1
  416. package/sidecar/dist/mcp/types.d.ts +2 -0
  417. package/sidecar/dist/metrics/aggregate.d.ts +13 -10
  418. package/sidecar/dist/metrics/aggregate.js +140 -143
  419. package/sidecar/dist/metrics/aggregate.js.map +1 -1
  420. package/sidecar/dist/metrics/types.d.ts +36 -23
  421. package/sidecar/dist/metrics/types.js +3 -1
  422. package/sidecar/dist/metrics/types.js.map +1 -1
  423. package/sidecar/dist/pages/imports-scanner.d.ts +1 -1
  424. package/sidecar/dist/pages/imports-scanner.js +2 -5
  425. package/sidecar/dist/pages/imports-scanner.js.map +1 -1
  426. package/sidecar/dist/prompts/agent.d.ts +1 -3
  427. package/sidecar/dist/prompts/agent.js +4 -4
  428. package/sidecar/dist/prompts/agent.js.map +1 -1
  429. package/sidecar/dist/prompts/builder.d.ts +2 -2
  430. package/sidecar/dist/prompts/builder.js +2 -2
  431. package/sidecar/dist/protocol/events.d.ts +1084 -18
  432. package/sidecar/dist/protocol/events.js +239 -5
  433. package/sidecar/dist/protocol/events.js.map +1 -1
  434. package/sidecar/dist/protocol/messages.d.ts +273 -19
  435. package/sidecar/dist/protocol/messages.js +107 -7
  436. package/sidecar/dist/protocol/messages.js.map +1 -1
  437. package/sidecar/dist/providers/claude/config.d.ts +3 -2
  438. package/sidecar/dist/providers/claude/config.js +10 -4
  439. package/sidecar/dist/providers/claude/config.js.map +1 -1
  440. package/sidecar/dist/providers/claude/provider.d.ts +2 -8
  441. package/sidecar/dist/providers/claude/provider.js +37 -35
  442. package/sidecar/dist/providers/claude/provider.js.map +1 -1
  443. package/sidecar/dist/providers/claude/safe-mode.d.ts +6 -1
  444. package/sidecar/dist/providers/claude/safe-mode.js +13 -5
  445. package/sidecar/dist/providers/claude/safe-mode.js.map +1 -1
  446. package/sidecar/dist/providers/codex/adapter.d.ts +6 -0
  447. package/sidecar/dist/providers/codex/adapter.js +14 -4
  448. package/sidecar/dist/providers/codex/adapter.js.map +1 -1
  449. package/sidecar/dist/providers/codex/client.d.ts +0 -11
  450. package/sidecar/dist/providers/codex/config.d.ts +1 -3
  451. package/sidecar/dist/providers/codex/config.js +5 -11
  452. package/sidecar/dist/providers/codex/config.js.map +1 -1
  453. package/sidecar/dist/providers/codex/permissions.d.ts +7 -4
  454. package/sidecar/dist/providers/codex/permissions.js +32 -16
  455. package/sidecar/dist/providers/codex/permissions.js.map +1 -1
  456. package/sidecar/dist/providers/codex/process.js +1 -5
  457. package/sidecar/dist/providers/codex/process.js.map +1 -1
  458. package/sidecar/dist/providers/codex/provider.js +14 -3
  459. package/sidecar/dist/providers/codex/provider.js.map +1 -1
  460. package/sidecar/dist/providers/codex/skills.d.ts +0 -1
  461. package/sidecar/dist/providers/codex/skills.js +1 -1
  462. package/sidecar/dist/providers/codex/skills.js.map +1 -1
  463. package/sidecar/dist/providers/codex/stop.js +1 -10
  464. package/sidecar/dist/providers/codex/stop.js.map +1 -1
  465. package/sidecar/dist/server/change-set-actions.d.ts +10 -0
  466. package/sidecar/dist/server/change-set-actions.js +14 -0
  467. package/sidecar/dist/server/change-set-actions.js.map +1 -0
  468. package/sidecar/dist/server/chat-events.d.ts +15 -0
  469. package/sidecar/dist/server/chat-events.js +115 -0
  470. package/sidecar/dist/server/chat-events.js.map +1 -0
  471. package/sidecar/dist/server/chat-socket-registry.d.ts +2 -0
  472. package/sidecar/dist/server/chat-socket-registry.js +48 -37
  473. package/sidecar/dist/server/chat-socket-registry.js.map +1 -1
  474. package/sidecar/dist/server/conversation-runs.d.ts +13 -0
  475. package/sidecar/dist/server/conversation-runs.js +114 -0
  476. package/sidecar/dist/server/conversation-runs.js.map +1 -0
  477. package/sidecar/dist/server/http-io.d.ts +4 -0
  478. package/sidecar/dist/server/http-io.js +31 -0
  479. package/sidecar/dist/server/http-io.js.map +1 -0
  480. package/sidecar/dist/server/http-params.d.ts +9 -0
  481. package/sidecar/dist/server/http-params.js +79 -0
  482. package/sidecar/dist/server/http-params.js.map +1 -0
  483. package/sidecar/dist/server/http-routes.d.ts +27 -0
  484. package/sidecar/dist/server/http-routes.js +281 -0
  485. package/sidecar/dist/server/http-routes.js.map +1 -0
  486. package/sidecar/dist/server/http.d.ts +6 -1
  487. package/sidecar/dist/server/http.js +76 -164
  488. package/sidecar/dist/server/http.js.map +1 -1
  489. package/sidecar/dist/server/pending-queue.d.ts +2 -0
  490. package/sidecar/dist/server/pending-queue.js +16 -0
  491. package/sidecar/dist/server/pending-queue.js.map +1 -1
  492. package/sidecar/dist/server/permission-events.d.ts +15 -0
  493. package/sidecar/dist/server/permission-events.js +163 -0
  494. package/sidecar/dist/server/permission-events.js.map +1 -0
  495. package/sidecar/dist/server/routing.d.ts +4 -52
  496. package/sidecar/dist/server/routing.js +190 -501
  497. package/sidecar/dist/server/routing.js.map +1 -1
  498. package/sidecar/dist/server/safety-net.d.ts +7 -0
  499. package/sidecar/dist/server/safety-net.js +34 -6
  500. package/sidecar/dist/server/safety-net.js.map +1 -1
  501. package/sidecar/dist/server/services.d.ts +55 -0
  502. package/sidecar/dist/server/services.js +2 -0
  503. package/sidecar/dist/server/services.js.map +1 -0
  504. package/sidecar/dist/server/tool-calls.d.ts +6 -0
  505. package/sidecar/dist/server/tool-calls.js +134 -0
  506. package/sidecar/dist/server/tool-calls.js.map +1 -0
  507. package/sidecar/dist/server/turn-registry.d.ts +44 -0
  508. package/sidecar/dist/server/turn-registry.js +40 -0
  509. package/sidecar/dist/server/turn-registry.js.map +1 -0
  510. package/sidecar/dist/server/turns.d.ts +17 -0
  511. package/sidecar/dist/server/turns.js +335 -0
  512. package/sidecar/dist/server/turns.js.map +1 -0
  513. package/sidecar/dist/server/ws.d.ts +4 -1
  514. package/sidecar/dist/server/ws.js +102 -62
  515. package/sidecar/dist/server/ws.js.map +1 -1
  516. package/sidecar/dist/skills/build-catalog.d.ts +1 -0
  517. package/sidecar/dist/skills/build-catalog.js +14 -1
  518. package/sidecar/dist/skills/build-catalog.js.map +1 -1
  519. package/sidecar/dist/skills/skill-usage.d.ts +22 -0
  520. package/sidecar/dist/skills/skill-usage.js +97 -0
  521. package/sidecar/dist/skills/skill-usage.js.map +1 -0
  522. package/sidecar/dist/state/change-sets.d.ts +12 -0
  523. package/sidecar/dist/state/change-sets.js +77 -0
  524. package/sidecar/dist/state/change-sets.js.map +1 -0
  525. package/sidecar/dist/state/conversations.d.ts +19 -7
  526. package/sidecar/dist/state/conversations.js +61 -20
  527. package/sidecar/dist/state/conversations.js.map +1 -1
  528. package/sidecar/dist/state/host-state.d.ts +2 -1
  529. package/sidecar/dist/state/settings-store.d.ts +1 -0
  530. package/sidecar/dist/state/settings-store.js +27 -26
  531. package/sidecar/dist/state/settings-store.js.map +1 -1
  532. package/sidecar/dist/state/settings-types.d.ts +11 -1
  533. package/sidecar/dist/state/settings-types.js +3 -1
  534. package/sidecar/dist/state/settings-types.js.map +1 -1
  535. package/sidecar/dist/state/types.d.ts +114 -8
  536. package/sidecar/dist/state/types.js +6 -3
  537. package/sidecar/dist/state/types.js.map +1 -1
  538. package/skills/dms-builder-safe/REFERENCE.md +1 -1
  539. package/skills/dms-builder-safe/SKILL.md +12 -7
  540. package/dist/routes/proxy.d.ts +0 -4
  541. package/dist/routes/proxy.js +0 -55
  542. package/dist/routes/proxy.js.map +0 -1
  543. package/frontend-vue/app/chat/components/ComposerInput.vue +0 -742
  544. package/frontend-vue/app/chat/components/PermissionRequests.vue +0 -323
  545. package/frontend-vue/app/chat/components/QuestionPrompt.vue +0 -275
  546. package/frontend-vue/app/chat/components/QueuedMessages.vue +0 -109
  547. package/frontend-vue/app/chat/components/TodoList.vue +0 -291
  548. package/frontend-vue/app/chat/components/ToolCallEntry.vue +0 -218
  549. package/frontend-vue/app/chat/constants/connection-banner.ts +0 -7
  550. package/frontend-vue/app/chat/constants/conversation-drawer.ts +0 -13
  551. package/frontend-vue/app/chat/constants/permissions.ts +0 -50
  552. package/frontend-vue/app/chat/utils/tool-summary.ts +0 -53
  553. package/frontend-vue/app/components/ActivityView.vue +0 -94
  554. package/frontend-vue/app/components/SettingsView.vue +0 -387
  555. package/frontend-vue/app/components/SkillsView.vue +0 -230
  556. package/frontend-vue/app/runtime/header-action.ts +0 -64
  557. package/frontend-vue/app/runtime/overlay-prefs.ts +0 -89
  558. package/frontend-vue/app/runtime/panel-state.ts +0 -88
  559. package/frontend-vue/tests/overlay-prefs.test.ts +0 -91
  560. package/frontend-vue/tests/panel-state.test.ts +0 -58
@@ -0,0 +1,806 @@
1
+ import { flushPromises } from "@vue/test-utils";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { nextTick } from "vue";
4
+ import {
5
+ buttonWithText,
6
+ CONVERSATION_ID,
7
+ createHarness,
8
+ deliver,
9
+ openPanel,
10
+ sendMessage,
11
+ sentOfType,
12
+ unmountPanel,
13
+ } from "./support/panel-harness";
14
+
15
+ const DIFF_REQUEST = {
16
+ type: "permission_request",
17
+ requestId: "r1",
18
+ callId: "c1",
19
+ toolName: "Edit",
20
+ args: { file_path: "/repo/crm/customers/customer.form.ts" },
21
+ summary: "Edit customer.form.ts",
22
+ kind: "edit",
23
+ alwaysAsk: false,
24
+ preview: {
25
+ type: "diff",
26
+ path: "/repo/crm/customers/customer.form.ts",
27
+ relativePath: "crm/customers/customer.form.ts",
28
+ isNewFile: false,
29
+ added: 1,
30
+ removed: 1,
31
+ hunks: [
32
+ {
33
+ oldStart: 42,
34
+ newStart: 42,
35
+ lines: [
36
+ { kind: "remove", text: 'label: "VAT",' },
37
+ { kind: "add", text: 'label: "VAT number",' },
38
+ ],
39
+ },
40
+ ],
41
+ },
42
+ ruleOptions: [
43
+ { kind: "file", value: "crm/customers/customer.form.ts" },
44
+ { kind: "directory", value: "crm/customers/" },
45
+ ],
46
+ createdAtMs: Date.now(),
47
+ expiresAtMs: Date.now() + 252_000,
48
+ };
49
+
50
+ const COMMAND_REQUEST = {
51
+ ...DIFF_REQUEST,
52
+ requestId: "r2",
53
+ callId: "c2",
54
+ toolName: "Bash",
55
+ args: { command: "pnpm add jsvat" },
56
+ summary: "Run pnpm add jsvat",
57
+ kind: "command",
58
+ preview: {
59
+ type: "command",
60
+ command: "pnpm add jsvat",
61
+ cwd: "/repo",
62
+ effect: "adds_dependency",
63
+ touches: ["package.json", "pnpm-lock.yaml"],
64
+ },
65
+ ruleOptions: [{ kind: "command", value: "pnpm add" }],
66
+ };
67
+
68
+ const DELETE_REQUEST = {
69
+ ...DIFF_REQUEST,
70
+ requestId: "r3",
71
+ callId: "c3",
72
+ toolName: "mcp__dms-ai__BuilderDeleteResource",
73
+ args: { ref: "legacy_coupons" },
74
+ summary: "Delete the table legacy_coupons",
75
+ kind: "destructive",
76
+ alwaysAsk: true,
77
+ preview: {
78
+ type: "destructive",
79
+ operation: "resource · API routes",
80
+ target: "legacy_coupons",
81
+ consequence: "deletes_data",
82
+ confirmText: "legacy_coupons",
83
+ canKeepData: true,
84
+ },
85
+ ruleOptions: [],
86
+ };
87
+
88
+ const CHANGE_SET = {
89
+ id: "cs-14",
90
+ number: 14,
91
+ conversationId: CONVERSATION_ID,
92
+ title: "Top customers",
93
+ createdAtMs: Date.now(),
94
+ agent: "claude",
95
+ scope: "safe",
96
+ isAutoFix: false,
97
+ overlapped: false,
98
+ files: [
99
+ {
100
+ path: "sales/overview.page.ts",
101
+ status: "modified",
102
+ added: 14,
103
+ removed: 1,
104
+ },
105
+ {
106
+ path: "sales/queries/top-customers.ts",
107
+ status: "added",
108
+ added: 22,
109
+ removed: 0,
110
+ },
111
+ ],
112
+ added: 36,
113
+ removed: 1,
114
+ typecheck: "passed",
115
+ state: "applied",
116
+ approvalsNeeded: 0,
117
+ builderOps: 2,
118
+ };
119
+
120
+ function keydown(target: Element, key: string): void {
121
+ target.dispatchEvent(new KeyboardEvent("keydown", { key, bubbles: true }));
122
+ }
123
+
124
+ beforeEach(() => {
125
+ localStorage.setItem("dms-ai-conversation-id", CONVERSATION_ID);
126
+ });
127
+
128
+ afterEach(() => {
129
+ unmountPanel();
130
+ localStorage.clear();
131
+ vi.useRealTimers();
132
+ });
133
+
134
+ describe("the panel's header and empty chat", () => {
135
+ it("invites a first request about the page on screen", async () => {
136
+ const harness = createHarness();
137
+ const panel = await openPanel(harness);
138
+ expect(panel.find(".cb-empty h3").text()).toBe(
139
+ "What should we change on Sales overview?",
140
+ );
141
+ await buttonWithText(panel.find(".cb-suggest"), "Add a chart").trigger(
142
+ "click",
143
+ );
144
+ expect((panel.find("textarea").element as HTMLTextAreaElement).value).toBe(
145
+ "Add a chart to Sales overview",
146
+ );
147
+ expect(panel.find(".att.is-page").text()).toContain("sales/overview");
148
+ expect(panel.find(".cb__status").text()).toBe("Ready · Claude Code");
149
+ });
150
+
151
+ it("says it works with the turn's clock, then waits for the user", async () => {
152
+ const harness = createHarness();
153
+ const panel = await openPanel(harness);
154
+ await sendMessage(panel, "go");
155
+ deliver(harness, {
156
+ type: "run_progress",
157
+ activity: "tool",
158
+ elapsedMs: 14_000,
159
+ idleMs: 0,
160
+ });
161
+ await nextTick();
162
+ expect(panel.find(".cb__status").text()).toContain("Working · 0:14");
163
+ deliver(harness, DIFF_REQUEST);
164
+ await nextTick();
165
+ expect(panel.find(".cb__status").text()).toContain(
166
+ "Waiting for you · 1 request",
167
+ );
168
+ });
169
+
170
+ it("sends leave_conversation when the panel closes", async () => {
171
+ const harness = createHarness();
172
+ await openPanel(harness);
173
+ harness.session.panel.close();
174
+ await nextTick();
175
+ expect(sentOfType(harness, "leave_conversation")).toEqual([
176
+ { type: "leave_conversation", conversationId: CONVERSATION_ID },
177
+ ]);
178
+ });
179
+
180
+ it("stamps a message with the page unless its chip was removed", async () => {
181
+ const harness = createHarness();
182
+ const panel = await openPanel(harness);
183
+ await panel
184
+ .find('button[aria-label="Remove page context"]')
185
+ .trigger("click");
186
+ await sendMessage(panel, "explain");
187
+ expect(sentOfType(harness, "user_message")[0]).toMatchObject({
188
+ content: "explain",
189
+ includePageContext: false,
190
+ });
191
+ expect(panel.find(".cb-user__meta").text()).toContain("sales/overview");
192
+ });
193
+ });
194
+
195
+ describe("tool rows and change sets", () => {
196
+ it("draws each call with the lexicon's words and its state", async () => {
197
+ const harness = createHarness();
198
+ const panel = await openPanel(harness);
199
+ await sendMessage(panel, "go");
200
+ deliver(harness, {
201
+ type: "tool_call_start",
202
+ callId: "c0",
203
+ toolName: "Read",
204
+ args: { file_path: "/a/customer.form.ts" },
205
+ });
206
+ deliver(harness, {
207
+ type: "tool_call_end",
208
+ callId: "c0",
209
+ status: "success",
210
+ result: "",
211
+ outcome: "done",
212
+ });
213
+ deliver(harness, {
214
+ type: "tool_call_start",
215
+ callId: "c9",
216
+ toolName: "Bash",
217
+ args: { command: "pnpm add dayjs" },
218
+ });
219
+ deliver(harness, {
220
+ type: "tool_call_end",
221
+ callId: "c9",
222
+ status: "error",
223
+ result: "",
224
+ outcome: "denied",
225
+ });
226
+ deliver(harness, {
227
+ type: "tool_call_start",
228
+ callId: "c1",
229
+ toolName: "Edit",
230
+ args: { file_path: "/repo/crm/customers/customer.form.ts" },
231
+ });
232
+ deliver(harness, DIFF_REQUEST);
233
+ await nextTick();
234
+ const rows = panel.findAll(".cb-tool");
235
+ expect(
236
+ rows.map((row) => row.classes().find((name) => name.startsWith("is-"))),
237
+ ).toEqual(["is-done", "is-denied", "is-waiting"]);
238
+ expect(rows[0].text()).toContain("Read file");
239
+ expect(rows[0].text()).toContain("customer.form.ts");
240
+ expect(rows[2].text()).toContain("1 of 1 · below");
241
+ expect(panel.find(".cb-tools__head").text()).toContain("Used 3 tools");
242
+ });
243
+
244
+ it("shows the raw JSON of a call on demand", async () => {
245
+ const harness = createHarness();
246
+ const panel = await openPanel(harness);
247
+ await sendMessage(panel, "go");
248
+ deliver(harness, {
249
+ type: "tool_call_start",
250
+ callId: "c0",
251
+ toolName: "mcp__dms-ai__BuilderAddBlock",
252
+ args: { name: "Top customers", type: "TopListCard" },
253
+ });
254
+ deliver(harness, {
255
+ type: "tool_call_end",
256
+ callId: "c0",
257
+ status: "success",
258
+ result: "ok",
259
+ outcome: "done",
260
+ });
261
+ await nextTick();
262
+ await panel.find(".cb-tool").trigger("click");
263
+ expect(panel.find(".cb-kv").text()).toContain("TopListCard");
264
+ await buttonWithText(panel, "Raw JSON").trigger("click");
265
+ expect(panel.find(".cb-raw").text()).toContain('"type": "TopListCard"');
266
+ });
267
+
268
+ it("ends a turn with its change set: undo, review, then redo", async () => {
269
+ const harness = createHarness();
270
+ const panel = await openPanel(harness);
271
+ await sendMessage(panel, "go");
272
+ deliver(harness, { type: "change_set", changeSet: CHANGE_SET });
273
+ deliver(harness, { type: "run_done" });
274
+ await nextTick();
275
+ const card = panel.find(".cb-change");
276
+ expect(card.text()).toContain("Applied · checkpoint 14");
277
+ expect(card.text()).toContain("2 files changed · 2 Builder operations");
278
+ expect(card.text()).toContain("new");
279
+ await buttonWithText(card, "Review diff").trigger("click");
280
+ expect(harness.navigate).toHaveBeenCalledWith(
281
+ "/modules/ai/changes?set=cs-14",
282
+ );
283
+ await buttonWithText(card, "Undo").trigger("click");
284
+ await flushPromises();
285
+ expect(sentOfType(harness, "change_set_action")).toEqual([
286
+ {
287
+ type: "change_set_action",
288
+ conversationId: CONVERSATION_ID,
289
+ changeSetId: "cs-14",
290
+ action: "undo",
291
+ },
292
+ ]);
293
+ deliver(harness, {
294
+ type: "change_set",
295
+ changeSet: { ...CHANGE_SET, state: "undone", stateChangedBy: "Camille" },
296
+ });
297
+ await nextTick();
298
+ expect(panel.find(".cb-change").text()).toContain(
299
+ "Undone · checkpoint 14 restored",
300
+ );
301
+ expect(panel.find(".cb-change").text()).toContain("by Camille");
302
+ await buttonWithText(panel.find(".cb-change"), "Redo").trigger("click");
303
+ expect(sentOfType(harness, "change_set_action").at(-1)).toMatchObject({
304
+ action: "redo",
305
+ });
306
+ });
307
+
308
+ it("asks before undoing a set later ones build on", async () => {
309
+ const harness = createHarness();
310
+ vi.mocked(harness.api.get).mockResolvedValueOnce({
311
+ conflicts: [
312
+ {
313
+ changeSetId: "cs-15",
314
+ number: 15,
315
+ title: "Refund rate",
316
+ files: ["sales/overview.page.ts"],
317
+ },
318
+ ],
319
+ });
320
+ const panel = await openPanel(harness);
321
+ deliver(harness, { type: "change_set", changeSet: CHANGE_SET });
322
+ await nextTick();
323
+ await buttonWithText(panel.find(".cb-change"), "Undo").trigger("click");
324
+ await flushPromises();
325
+ expect(panel.find(".u-modal").text()).toContain("#15 Refund rate");
326
+ await buttonWithText(panel.find(".u-modal"), "Undo them together").trigger(
327
+ "click",
328
+ );
329
+ expect(sentOfType(harness, "change_set_action")[0]).toMatchObject({
330
+ includeLater: true,
331
+ });
332
+ });
333
+ });
334
+
335
+ describe("the approvals dock", () => {
336
+ it("previews a diff with line numbers and allows a scope with ↵", async () => {
337
+ const harness = createHarness();
338
+ const panel = await openPanel(harness);
339
+ deliver(harness, DIFF_REQUEST);
340
+ deliver(harness, COMMAND_REQUEST);
341
+ await nextTick();
342
+ const dock = panel.find(".cb-approve");
343
+ expect(dock.attributes("role")).toBe("alertdialog");
344
+ expect(dock.text()).toContain("Needs your approval · 1 of 2");
345
+ expect(dock.text()).toMatch(/denies in 4:1\d/);
346
+ expect(panel.find(".perm.is-focused .perm__title").text()).toBe(
347
+ "Edit customer.form.ts",
348
+ );
349
+ expect(panel.findAll(".ln").map((row) => row.text())).toEqual([
350
+ "@@ −42 +42 @@",
351
+ '42−label: "VAT",',
352
+ '42+label: "VAT number",',
353
+ ]);
354
+ expect(dock.text()).toContain("Every edit under crm/customers/");
355
+ await panel.findAll(".perm.is-focused .scope-opt input")[2].setValue(true);
356
+ keydown(dock.element, "Enter");
357
+ await nextTick();
358
+ expect(sentOfType(harness, "permission_response")[0]).toEqual({
359
+ type: "permission_response",
360
+ conversationId: CONVERSATION_ID,
361
+ requestId: "r1",
362
+ decision: "allow_rule",
363
+ rule: { kind: "directory", value: "crm/customers/" },
364
+ });
365
+ expect(panel.find(".perm.is-focused .perm__title").text()).toBe(
366
+ "Run pnpm add jsvat",
367
+ );
368
+ expect(panel.find(".perm.is-focused").text()).toContain(
369
+ "adds a dependency · package.json, pnpm-lock.yaml",
370
+ );
371
+ });
372
+
373
+ it("moves with J and K, denies with N, and denies everything at once", async () => {
374
+ const harness = createHarness();
375
+ const panel = await openPanel(harness);
376
+ deliver(harness, DIFF_REQUEST);
377
+ deliver(harness, COMMAND_REQUEST);
378
+ await nextTick();
379
+ const dock = panel.find(".cb-approve");
380
+ keydown(dock.element, "j");
381
+ await nextTick();
382
+ expect(dock.text()).toContain("2 of 2");
383
+ keydown(dock.element, "k");
384
+ await nextTick();
385
+ expect(dock.text()).toContain("1 of 2");
386
+ keydown(dock.element, "n");
387
+ await nextTick();
388
+ expect(sentOfType(harness, "permission_response")[0]).toMatchObject({
389
+ requestId: "r1",
390
+ decision: "deny",
391
+ });
392
+ deliver(harness, DIFF_REQUEST);
393
+ await nextTick();
394
+ await buttonWithText(
395
+ panel.find(".cb-approve"),
396
+ "Deny both and stop",
397
+ ).trigger("click");
398
+ expect(sentOfType(harness, "permission_response").at(-1)).toMatchObject({
399
+ decision: "deny_all",
400
+ });
401
+ });
402
+
403
+ it("denies with the user's suggestion", async () => {
404
+ const harness = createHarness();
405
+ const panel = await openPanel(harness);
406
+ deliver(harness, DIFF_REQUEST);
407
+ await nextTick();
408
+ await buttonWithText(panel, "Suggest a change").trigger("click");
409
+ await panel
410
+ .find(".u-textarea")
411
+ .setValue("Use the Builder's validation instead");
412
+ await buttonWithText(panel, "Deny and send").trigger("click");
413
+ expect(sentOfType(harness, "permission_response")[0]).toMatchObject({
414
+ decision: "deny",
415
+ feedback: "Use the Builder's validation instead",
416
+ });
417
+ });
418
+
419
+ it("makes a data loss typed, offers to keep the data, and never a chat-wide rule", async () => {
420
+ const harness = createHarness();
421
+ const panel = await openPanel(harness);
422
+ deliver(harness, DELETE_REQUEST);
423
+ await nextTick();
424
+ const card = panel.find(".perm.is-danger");
425
+ expect(card.text()).toContain("Data loss");
426
+ expect(card.text()).not.toContain("this chat");
427
+ const confirm = buttonWithText(card, "Delete");
428
+ expect(confirm.attributes("disabled")).toBeDefined();
429
+ await card.find(".perm__confirm input").setValue("legacy_coupons");
430
+ expect(
431
+ buttonWithText(card, "Delete").attributes("disabled"),
432
+ ).toBeUndefined();
433
+ await card.find('input[type="checkbox"]').setValue(true);
434
+ await buttonWithText(card, "Remove the code only").trigger("click");
435
+ expect(sentOfType(harness, "permission_response")[0]).toMatchObject({
436
+ decision: "allow_once",
437
+ keepData: true,
438
+ });
439
+ });
440
+
441
+ it("turns an expired request into a card that asks again", async () => {
442
+ const harness = createHarness();
443
+ const panel = await openPanel(harness);
444
+ deliver(harness, DIFF_REQUEST);
445
+ deliver(harness, {
446
+ type: "permission_expired",
447
+ requestId: "r1",
448
+ toolName: "Edit",
449
+ summary: "Edit customer.form.ts",
450
+ expiredAtMs: Date.now(),
451
+ });
452
+ await nextTick();
453
+ expect(panel.find(".cb-approve").exists()).toBe(false);
454
+ const card = panel.find(".cb-expired");
455
+ expect(card.text()).toContain("Edit file request expired");
456
+ await buttonWithText(card, "Ask again").trigger("click");
457
+ expect(sentOfType(harness, "user_message")[0].content).toContain(
458
+ "Edit customer.form.ts",
459
+ );
460
+ expect(panel.find(".cb-expired").exists()).toBe(false);
461
+ });
462
+
463
+ it("lists the rules allowed in this chat and revokes one", async () => {
464
+ const harness = createHarness();
465
+ const panel = await openPanel(harness);
466
+ deliver(harness, DIFF_REQUEST);
467
+ deliver(harness, {
468
+ type: "rules_state",
469
+ rules: [
470
+ {
471
+ id: "rule-1",
472
+ kind: "command",
473
+ value: "pnpm test",
474
+ label: "Commands starting pnpm test",
475
+ createdAtMs: 1,
476
+ },
477
+ ],
478
+ });
479
+ await nextTick();
480
+ const rules = panel.find(".cb-approve .cb-rules");
481
+ expect(rules.text()).toContain("Commands starting pnpm test");
482
+ await buttonWithText(rules, "Revoke").trigger("click");
483
+ expect(sentOfType(harness, "revoke_rule")).toEqual([
484
+ {
485
+ type: "revoke_rule",
486
+ conversationId: CONVERSATION_ID,
487
+ ruleId: "rule-1",
488
+ },
489
+ ]);
490
+ });
491
+ });
492
+
493
+ describe("questions, queue and modes", () => {
494
+ const QUESTIONS = {
495
+ type: "ask_question",
496
+ requestId: "q1",
497
+ questions: [
498
+ {
499
+ question: "Where should the page live?",
500
+ header: "Placement",
501
+ options: [
502
+ {
503
+ label: "Finance › Overdue invoices",
504
+ description: "Next to Invoices",
505
+ },
506
+ { label: "A tab on Invoices", description: "" },
507
+ ],
508
+ },
509
+ {
510
+ question: "Default sort?",
511
+ header: "Sort",
512
+ options: [
513
+ { label: "Days overdue", description: "" },
514
+ { label: "Amount", description: "" },
515
+ ],
516
+ },
517
+ ],
518
+ };
519
+
520
+ it("pages through questions with numbers, Other… and Skip, then keeps the answers", async () => {
521
+ const harness = createHarness();
522
+ const panel = await openPanel(harness);
523
+ deliver(harness, QUESTIONS);
524
+ await nextTick();
525
+ const dock = panel.find(".cb-ask");
526
+ expect(dock.text()).toContain("Question 1 of 2 · Placement");
527
+ expect(dock.find(".opt-card.is-selected").text()).toContain("Suggested");
528
+ keydown(dock.element, "3");
529
+ await nextTick();
530
+ await dock.find(".cb-ask__custom").setValue("Under Finance, as a tab");
531
+ await buttonWithText(dock.find(".cb-ask__foot"), "Next").trigger("click");
532
+ expect(dock.text()).toContain("Question 2 of 2 · Sort");
533
+ await buttonWithText(dock, "Skip, you decide").trigger("click");
534
+ expect(sentOfType(harness, "question_response")[0]).toEqual({
535
+ type: "question_response",
536
+ conversationId: CONVERSATION_ID,
537
+ requestId: "q1",
538
+ answers: ["Under Finance, as a tab", ""],
539
+ skipped: [false, true],
540
+ });
541
+ await nextTick();
542
+ const record = panel.find(".cb-answers");
543
+ expect(record.text()).toContain("“Under Finance, as a tab”");
544
+ expect(record.text()).toContain("Skipped: the assistant decides");
545
+ });
546
+
547
+ it("edits, reorders and clears the queue", async () => {
548
+ const harness = createHarness();
549
+ const panel = await openPanel(harness);
550
+ deliver(harness, {
551
+ type: "queue_state",
552
+ items: [
553
+ { id: "a", content: "Sort by order count" },
554
+ { id: "b", content: "Limit to 3 orders" },
555
+ ],
556
+ });
557
+ await nextTick();
558
+ await panel
559
+ .find('button[aria-label="Edit queued message"]')
560
+ .trigger("click");
561
+ await panel.find(".cb-queued__editor").setValue("Sort by revenue");
562
+ await buttonWithText(panel.find(".cb-queue"), "Save").trigger("click");
563
+ keydown(panel.findAll(".cb-queued__handle")[1].element, "ArrowUp");
564
+ await nextTick();
565
+ expect(panel.findAll(".cb-queued__txt").map((item) => item.text())).toEqual(
566
+ ["Limit to 3 orders", "Sort by revenue"],
567
+ );
568
+ await buttonWithText(panel.find(".cb-queue"), "Clear").trigger("click");
569
+ expect(
570
+ harness.sent
571
+ .map((msg) => msg.type)
572
+ .filter((type) => String(type).startsWith("queue_")),
573
+ ).toEqual(["queue_update", "queue_move", "queue_clear"]);
574
+ });
575
+
576
+ it("switches the chat's mode and scope, and confirms Full auto with a duration", async () => {
577
+ const harness = createHarness();
578
+ const panel = await openPanel(harness);
579
+ await buttonWithText(panel, "Plan only").trigger("click");
580
+ await buttonWithText(panel, "Full auto…").trigger("click");
581
+ await nextTick();
582
+ const modal = panel.find(".u-modal");
583
+ expect(modal.text()).toContain("Turn on Full auto?");
584
+ await buttonWithText(modal, "30 minutes").trigger("click");
585
+ await buttonWithText(modal, "Turn on Full auto").trigger("click");
586
+ expect(sentOfType(harness, "set_conversation_mode")).toEqual([
587
+ {
588
+ type: "set_conversation_mode",
589
+ conversationId: CONVERSATION_ID,
590
+ mode: "plan",
591
+ },
592
+ {
593
+ type: "set_conversation_mode",
594
+ conversationId: CONVERSATION_ID,
595
+ fullAuto: { duration: "30m" },
596
+ },
597
+ ]);
598
+ await nextTick();
599
+ const banner = panel.find(".cb-banner-auto");
600
+ expect(banner.text()).toContain("Full auto · for 30 minutes");
601
+ await buttonWithText(banner, "Turn off").trigger("click");
602
+ expect(sentOfType(harness, "set_conversation_mode").at(-1)).toMatchObject({
603
+ fullAuto: null,
604
+ });
605
+ });
606
+
607
+ it("runs a proposed plan in Ask first", async () => {
608
+ const harness = createHarness();
609
+ const panel = await openPanel(harness);
610
+ await sendMessage(panel, "plan a customer health page");
611
+ deliver(harness, {
612
+ type: "tool_call_start",
613
+ callId: "p1",
614
+ toolName: "ExitPlanMode",
615
+ args: { plan: "1. Create the page\n2. Add a query" },
616
+ });
617
+ deliver(harness, {
618
+ type: "tool_call_end",
619
+ callId: "p1",
620
+ status: "success",
621
+ result: "",
622
+ outcome: "done",
623
+ });
624
+ deliver(harness, { type: "run_done" });
625
+ await nextTick();
626
+ const plan = panel.find(".cb-plan");
627
+ expect(plan.text()).toContain("Proposed plan · 2 steps");
628
+ await buttonWithText(plan, "Run with Ask first").trigger("click");
629
+ expect(sentOfType(harness, "set_conversation_mode")[0]).toMatchObject({
630
+ mode: "normal",
631
+ });
632
+ expect(sentOfType(harness, "user_message").at(-1)).toMatchObject({
633
+ content: "Go ahead with the plan.",
634
+ });
635
+ });
636
+ });
637
+
638
+ describe("notices and errors", () => {
639
+ it("shows an auto-fix turn that can be stopped", async () => {
640
+ const harness = createHarness();
641
+ const panel = await openPanel(harness);
642
+ await sendMessage(panel, "go");
643
+ deliver(harness, {
644
+ type: "notice",
645
+ notice: {
646
+ kind: "autofix",
647
+ attempt: 1,
648
+ maxAttempts: 2,
649
+ errors: ["TS2339 in top-customers.ts"],
650
+ timestampMs: Date.now(),
651
+ },
652
+ });
653
+ await nextTick();
654
+ const notice = panel.find(".cb-notice");
655
+ expect(notice.text()).toContain("Auto-fix turn 1 of 2.");
656
+ await buttonWithText(notice, "Show error").trigger("click");
657
+ expect(notice.text()).toContain("TS2339");
658
+ await buttonWithText(notice, "Stop auto-fix").trigger("click");
659
+ expect(sentOfType(harness, "stop_autofix")).toHaveLength(1);
660
+ });
661
+
662
+ it("says the agent can't run, with no fallback, and points to the settings", async () => {
663
+ const harness = createHarness();
664
+ const panel = await openPanel(harness);
665
+ deliver(harness, {
666
+ type: "settings_update",
667
+ settings: {
668
+ provider: "codex",
669
+ mode: "normal",
670
+ thinking: "medium",
671
+ generationMode: "safe",
672
+ allowLocalSkills: false,
673
+ },
674
+ providers: {
675
+ claude: { available: true },
676
+ codex: { available: false, reason: "Set OPENAI_API_KEY." },
677
+ },
678
+ });
679
+ await nextTick();
680
+ const notice = panel.find(".cb-provider");
681
+ expect(notice.text()).toContain("Codex can't run.");
682
+ expect(panel.find("textarea").attributes("disabled")).toBeDefined();
683
+ await buttonWithText(notice, "Open settings").trigger("click");
684
+ expect(harness.navigate).toHaveBeenCalledWith("/modules/ai/settings");
685
+ });
686
+
687
+ it("stops the turn with Esc", async () => {
688
+ const harness = createHarness();
689
+ const panel = await openPanel(harness);
690
+ await sendMessage(panel, "go");
691
+ keydown(panel.find("textarea").element, "Escape");
692
+ expect(sentOfType(harness, "interrupt_turn")).toHaveLength(1);
693
+ });
694
+ });
695
+
696
+ describe("the history drawer", () => {
697
+ const LIST = {
698
+ type: "conversation_list",
699
+ conversations: [
700
+ {
701
+ id: "c-run",
702
+ title: "Add top customers",
703
+ createdAtMs: 1,
704
+ updatedAtMs: Date.now(),
705
+ messageCount: 4,
706
+ isRunning: true,
707
+ filesChanged: 2,
708
+ totalTokens: 9_600,
709
+ },
710
+ {
711
+ id: "c-old",
712
+ title: "Why is order pending?",
713
+ createdAtMs: 1,
714
+ updatedAtMs: Date.now() - 3_600_000,
715
+ messageCount: 2,
716
+ filesChanged: 0,
717
+ filesUndone: 1,
718
+ totalTokens: 1_300,
719
+ },
720
+ ],
721
+ };
722
+
723
+ it("opens with ⌘J, pins the running chat, and switches chats", async () => {
724
+ const harness = createHarness();
725
+ const panel = await openPanel(harness);
726
+ document.dispatchEvent(
727
+ new KeyboardEvent("keydown", { key: "j", metaKey: true }),
728
+ );
729
+ deliver(harness, LIST);
730
+ await nextTick();
731
+ const drawer = panel.find(".cb-drawer");
732
+ expect(
733
+ drawer.findAll(".cb-drawer__group").map((group) => group.text()),
734
+ ).toEqual(["Active · 1", "Today"]);
735
+ expect(drawer.text()).toContain("9.6k tok");
736
+ expect(drawer.text()).toContain("1 file undone");
737
+ await buttonWithText(drawer, "Why is order pending?").trigger("click");
738
+ expect(sentOfType(harness, "leave_conversation")[0]).toMatchObject({
739
+ conversationId: CONVERSATION_ID,
740
+ });
741
+ expect(sentOfType(harness, "hello").at(-1)).toMatchObject({
742
+ conversationId: "c-old",
743
+ });
744
+ });
745
+
746
+ it("deletes after 8 seconds unless undone, and asks first for a running chat", async () => {
747
+ vi.useFakeTimers();
748
+ const harness = createHarness();
749
+ const panel = await openPanel(harness);
750
+ document.dispatchEvent(
751
+ new KeyboardEvent("keydown", { key: "j", ctrlKey: true }),
752
+ );
753
+ deliver(harness, LIST);
754
+ await nextTick();
755
+ await panel
756
+ .findAll('button[aria-label="Delete conversation"]')[1]
757
+ .trigger("click");
758
+ expect(panel.find(".cb-toast").text()).toContain(
759
+ "Deleted “Why is order pending?”",
760
+ );
761
+ await buttonWithText(panel.find(".cb-toast"), "Undo").trigger("click");
762
+ await vi.advanceTimersByTimeAsync(9_000);
763
+ expect(sentOfType(harness, "delete_conversation")).toEqual([]);
764
+ await panel
765
+ .findAll('button[aria-label="Delete conversation"]')[1]
766
+ .trigger("click");
767
+ await vi.advanceTimersByTimeAsync(9_000);
768
+ expect(sentOfType(harness, "delete_conversation")).toEqual([
769
+ { type: "delete_conversation", conversationId: "c-old" },
770
+ ]);
771
+ await panel
772
+ .findAll('button[aria-label="Delete conversation"]')[0]
773
+ .trigger("click");
774
+ await buttonWithText(panel.find(".u-modal"), "Stop and delete").trigger(
775
+ "click",
776
+ );
777
+ expect(sentOfType(harness, "interrupt_turn")).toEqual([
778
+ { type: "interrupt_turn", conversationId: "c-run" },
779
+ ]);
780
+ });
781
+ });
782
+
783
+ describe("the dashboard can open the panel on a chat", () => {
784
+ it("starts a new chat with a prefilled composer", async () => {
785
+ const harness = createHarness();
786
+ const panel = await openPanel(harness);
787
+ harness.session.startConversation("Add a refund chart");
788
+ await flushPromises();
789
+ expect((panel.find("textarea").element as HTMLTextAreaElement).value).toBe(
790
+ "Add a refund chart",
791
+ );
792
+ expect(sentOfType(harness, "hello").at(-1)?.conversationId).not.toBe(
793
+ CONVERSATION_ID,
794
+ );
795
+ });
796
+
797
+ it("opens a stored chat", async () => {
798
+ const harness = createHarness();
799
+ await openPanel(harness);
800
+ harness.session.openConversation("c-42");
801
+ await nextTick();
802
+ expect(sentOfType(harness, "hello").at(-1)).toMatchObject({
803
+ conversationId: "c-42",
804
+ });
805
+ });
806
+ });