@antelopejs/dms-ai 0.6.0 → 0.7.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 (330) hide show
  1. package/README.md +2 -2
  2. package/dist/builder/presence.js.map +1 -1
  3. package/dist/channels/bridge.js +4 -2
  4. package/dist/channels/bridge.js.map +1 -1
  5. package/dist/channels/sse-stream.d.ts +1 -1
  6. package/dist/config.d.ts +6 -1
  7. package/dist/config.js +9 -21
  8. package/dist/config.js.map +1 -1
  9. package/dist/constants/channels.d.ts +5 -1
  10. package/dist/constants/channels.js.map +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/lifecycle/build-id.js +1 -1
  14. package/dist/lifecycle/build-id.js.map +1 -1
  15. package/dist/lifecycle/module-roots.js +12 -14
  16. package/dist/lifecycle/module-roots.js.map +1 -1
  17. package/dist/lifecycle/sidecar-owner.js.map +1 -1
  18. package/dist/lifecycle/skill-sources.js +35 -22
  19. package/dist/lifecycle/skill-sources.js.map +1 -1
  20. package/dist/lifecycle/spawn-sidecar.js +11 -6
  21. package/dist/lifecycle/spawn-sidecar.js.map +1 -1
  22. package/dist/logging/log-buffer.js +6 -2
  23. package/dist/logging/log-buffer.js.map +1 -1
  24. package/dist/pages/activity/columns.d.ts +66 -2
  25. package/dist/pages/activity/columns.js.map +1 -1
  26. package/dist/pages/activity/page.js.map +1 -1
  27. package/dist/pages/index.js.map +1 -1
  28. package/dist/pages/overview/page.js.map +1 -1
  29. package/dist/pages/overview/recent-changes.js.map +1 -1
  30. package/dist/pages/settings/availability.d.ts +1 -1
  31. package/dist/pages/settings/availability.js +19 -5
  32. package/dist/pages/settings/availability.js.map +1 -1
  33. package/dist/pages/settings/page.js +1 -1
  34. package/dist/pages/settings/page.js.map +1 -1
  35. package/dist/pages/skills/page.js.map +1 -1
  36. package/dist/pages-registry.js +9 -10
  37. package/dist/pages-registry.js.map +1 -1
  38. package/dist/routes/activity.d.ts +5 -4
  39. package/dist/routes/activity.js.map +1 -1
  40. package/dist/routes/changes.d.ts +6 -4
  41. package/dist/routes/changes.js.map +1 -1
  42. package/dist/routes/codex-account.d.ts +5 -1
  43. package/dist/routes/codex-account.js +2 -2
  44. package/dist/routes/codex-account.js.map +1 -1
  45. package/dist/routes/database.d.ts +21 -4
  46. package/dist/routes/database.js.map +1 -1
  47. package/dist/routes/metric-shapes.js +8 -5
  48. package/dist/routes/metric-shapes.js.map +1 -1
  49. package/dist/routes/metrics.d.ts +4 -2
  50. package/dist/routes/metrics.js +2 -2
  51. package/dist/routes/metrics.js.map +1 -1
  52. package/dist/routes/pages-registry.js.map +1 -1
  53. package/dist/routes/settings.d.ts +4 -3
  54. package/dist/routes/settings.js +4 -15
  55. package/dist/routes/settings.js.map +1 -1
  56. package/dist/routes/sidecar-info.js.map +1 -1
  57. package/dist/routes/sidecar-results.d.ts +2 -2
  58. package/dist/routes/sidecar-results.js +10 -10
  59. package/dist/routes/sidecar-results.js.map +1 -1
  60. package/dist/routes/skills.d.ts +2 -1
  61. package/dist/routes/skills.js +6 -4
  62. package/dist/routes/skills.js.map +1 -1
  63. package/dist/routes/status-shapes.js +12 -12
  64. package/dist/routes/status-shapes.js.map +1 -1
  65. package/dist/routes/undo-confirm.js.map +1 -1
  66. package/dist/sidecar/http-client.js.map +1 -1
  67. package/dist/sidecar/index.d.ts +1 -0
  68. package/dist/sidecar/index.js +1 -0
  69. package/dist/sidecar/index.js.map +1 -1
  70. package/dist/sidecar/settings-values.d.ts +3 -0
  71. package/dist/sidecar/settings-values.js +21 -0
  72. package/dist/sidecar/settings-values.js.map +1 -0
  73. package/dist/vocabulary/tools.js.map +1 -1
  74. package/frontend-vue/app/chat/components/ChatView.vue +33 -133
  75. package/frontend-vue/app/chat/components/ProviderNotice.vue +77 -0
  76. package/frontend-vue/app/chat/components/UndoConflictModal.vue +98 -0
  77. package/frontend-vue/app/chat/composables/useChangeSetActions.ts +1 -1
  78. package/frontend-vue/app/plugins/ai.client.ts +10 -2
  79. package/frontend-vue/app/runtime/assistant-actions.ts +41 -0
  80. package/frontend-vue/app/runtime/assistant-session.ts +8 -2
  81. package/frontend-vue/app/runtime/panel-intents.ts +3 -1
  82. package/frontend-vue/build/backend-boundary.ts +89 -0
  83. package/frontend-vue/dms.frontend.build.ts +6 -0
  84. package/frontend-vue/package.json +2 -1
  85. package/frontend-vue/pnpm-lock.yaml +3 -0
  86. package/frontend-vue/tests/assistant-actions.test.ts +72 -0
  87. package/frontend-vue/tests/backend-boundary.test.ts +130 -0
  88. package/frontend-vue/tests/panel-approvals.test.ts +190 -0
  89. package/frontend-vue/tests/panel-questions.test.ts +169 -0
  90. package/frontend-vue/tests/panel-states.test.ts +1 -556
  91. package/frontend-vue/tests/panel-tools.test.ts +165 -0
  92. package/frontend-vue/tests/sidecar-guest.test.ts +39 -71
  93. package/frontend-vue/tests/support/panel-fixtures.ts +110 -0
  94. package/frontend-vue/tests/support/panel-harness.ts +2 -2
  95. package/frontend-vue/vitest.config.ts +2 -1
  96. package/package.json +21 -10
  97. package/sidecar/dist/agent/attachment-files.js.map +1 -1
  98. package/sidecar/dist/agent/call-ledger.d.ts +3 -2
  99. package/sidecar/dist/agent/call-ledger.js +1 -1
  100. package/sidecar/dist/agent/call-ledger.js.map +1 -1
  101. package/sidecar/dist/agent/command-analysis.js +13 -14
  102. package/sidecar/dist/agent/command-analysis.js.map +1 -1
  103. package/sidecar/dist/agent/conversation-modes.js.map +1 -1
  104. package/sidecar/dist/agent/edit-targets.d.ts +5 -1
  105. package/sidecar/dist/agent/edit-targets.js +8 -6
  106. package/sidecar/dist/agent/edit-targets.js.map +1 -1
  107. package/sidecar/dist/agent/edit-tracker.d.ts +2 -1
  108. package/sidecar/dist/agent/edit-tracker.js +3 -4
  109. package/sidecar/dist/agent/edit-tracker.js.map +1 -1
  110. package/sidecar/dist/agent/effective-mode.js.map +1 -1
  111. package/sidecar/dist/agent/line-diff.js +10 -8
  112. package/sidecar/dist/agent/line-diff.js.map +1 -1
  113. package/sidecar/dist/agent/permission-bus.js +2 -1
  114. package/sidecar/dist/agent/permission-bus.js.map +1 -1
  115. package/sidecar/dist/agent/permission-preview.d.ts +1 -1
  116. package/sidecar/dist/agent/permission-preview.js +54 -37
  117. package/sidecar/dist/agent/permission-preview.js.map +1 -1
  118. package/sidecar/dist/agent/permission-rules.js +4 -6
  119. package/sidecar/dist/agent/permission-rules.js.map +1 -1
  120. package/sidecar/dist/agent/provider.d.ts +2 -1
  121. package/sidecar/dist/agent/question-bus.js.map +1 -1
  122. package/sidecar/dist/agent/read-access.d.ts +2 -1
  123. package/sidecar/dist/agent/read-access.js +4 -7
  124. package/sidecar/dist/agent/read-access.js.map +1 -1
  125. package/sidecar/dist/agent/runner.js.map +1 -1
  126. package/sidecar/dist/agent/session.js +2 -6
  127. package/sidecar/dist/agent/session.js.map +1 -1
  128. package/sidecar/dist/agent/switching-runner.js.map +1 -1
  129. package/sidecar/dist/agent/system-prompt.js.map +1 -1
  130. package/sidecar/dist/agent/tool-summary.d.ts +2 -1
  131. package/sidecar/dist/agent/tool-summary.js +26 -13
  132. package/sidecar/dist/agent/tool-summary.js.map +1 -1
  133. package/sidecar/dist/agent/typecheck.d.ts +1 -4
  134. package/sidecar/dist/agent/typecheck.js +3 -2
  135. package/sidecar/dist/agent/typecheck.js.map +1 -1
  136. package/sidecar/dist/audit/activity-query.js +4 -8
  137. package/sidecar/dist/audit/activity-query.js.map +1 -1
  138. package/sidecar/dist/audit/activity-target.d.ts +4 -2
  139. package/sidecar/dist/audit/activity-target.js +10 -9
  140. package/sidecar/dist/audit/activity-target.js.map +1 -1
  141. package/sidecar/dist/audit/activity.js +4 -3
  142. package/sidecar/dist/audit/activity.js.map +1 -1
  143. package/sidecar/dist/audit/call-audit.js.map +1 -1
  144. package/sidecar/dist/builder/builder-client.d.ts +2 -1
  145. package/sidecar/dist/builder/builder-client.js +6 -8
  146. package/sidecar/dist/builder/builder-client.js.map +1 -1
  147. package/sidecar/dist/builder/capability.js.map +1 -1
  148. package/sidecar/dist/checkpoints/checkpoints.js.map +1 -1
  149. package/sidecar/dist/checkpoints/shadow-git.js +6 -2
  150. package/sidecar/dist/checkpoints/shadow-git.js.map +1 -1
  151. package/sidecar/dist/constants/agent.js.map +1 -1
  152. package/sidecar/dist/constants/attachments.js.map +1 -1
  153. package/sidecar/dist/constants/builder.js.map +1 -1
  154. package/sidecar/dist/constants/chatgpt.js.map +1 -1
  155. package/sidecar/dist/constants/checkpoints.d.ts +1 -1
  156. package/sidecar/dist/constants/checkpoints.js +6 -6
  157. package/sidecar/dist/constants/checkpoints.js.map +1 -1
  158. package/sidecar/dist/constants/claude.d.ts +3 -4
  159. package/sidecar/dist/constants/claude.js +16 -16
  160. package/sidecar/dist/constants/claude.js.map +1 -1
  161. package/sidecar/dist/constants/cli.js.map +1 -1
  162. package/sidecar/dist/constants/codex.d.ts +4 -5
  163. package/sidecar/dist/constants/codex.js +25 -25
  164. package/sidecar/dist/constants/codex.js.map +1 -1
  165. package/sidecar/dist/constants/database.js.map +1 -1
  166. package/sidecar/dist/constants/mcp.js.map +1 -1
  167. package/sidecar/dist/constants/paths.js.map +1 -1
  168. package/sidecar/dist/constants/project.d.ts +1 -1
  169. package/sidecar/dist/constants/project.js.map +1 -1
  170. package/sidecar/dist/constants/providers.js.map +1 -1
  171. package/sidecar/dist/constants/questions.js.map +1 -1
  172. package/sidecar/dist/constants/read-access.d.ts +1 -1
  173. package/sidecar/dist/constants/read-access.js +7 -7
  174. package/sidecar/dist/constants/read-access.js.map +1 -1
  175. package/sidecar/dist/constants/timing.js.map +1 -1
  176. package/sidecar/dist/database/database-client.d.ts +3 -2
  177. package/sidecar/dist/database/database-client.js +13 -9
  178. package/sidecar/dist/database/database-client.js.map +1 -1
  179. package/sidecar/dist/host/project-info.js +13 -7
  180. package/sidecar/dist/host/project-info.js.map +1 -1
  181. package/sidecar/dist/index.d.ts +1 -1
  182. package/sidecar/dist/index.js +60 -34
  183. package/sidecar/dist/index.js.map +1 -1
  184. package/sidecar/dist/logs/logs-client.js +12 -2
  185. package/sidecar/dist/logs/logs-client.js.map +1 -1
  186. package/sidecar/dist/mcp/define-tool.d.ts +9 -8
  187. package/sidecar/dist/mcp/define-tool.js.map +1 -1
  188. package/sidecar/dist/mcp/http-binding.js.map +1 -1
  189. package/sidecar/dist/mcp/tools/ask-user.js.map +1 -1
  190. package/sidecar/dist/mcp/tools/builder-catalog.d.ts +6 -1
  191. package/sidecar/dist/mcp/tools/builder-catalog.js +32 -24
  192. package/sidecar/dist/mcp/tools/builder-catalog.js.map +1 -1
  193. package/sidecar/dist/mcp/tools/builder-gate.js.map +1 -1
  194. package/sidecar/dist/mcp/tools/builder-project.d.ts +2 -1
  195. package/sidecar/dist/mcp/tools/builder-project.js +60 -43
  196. package/sidecar/dist/mcp/tools/builder-project.js.map +1 -1
  197. package/sidecar/dist/mcp/tools/builder.js +8 -9
  198. package/sidecar/dist/mcp/tools/builder.js.map +1 -1
  199. package/sidecar/dist/mcp/tools/database.js +5 -9
  200. package/sidecar/dist/mcp/tools/database.js.map +1 -1
  201. package/sidecar/dist/mcp/tools/find-pages-using.js.map +1 -1
  202. package/sidecar/dist/mcp/tools/get-current-page.js.map +1 -1
  203. package/sidecar/dist/mcp/tools/list-pages.js.map +1 -1
  204. package/sidecar/dist/mcp/tools/navigate-to-page.js +6 -6
  205. package/sidecar/dist/mcp/tools/navigate-to-page.js.map +1 -1
  206. package/sidecar/dist/mcp/tools/preview-blocks.js +15 -11
  207. package/sidecar/dist/mcp/tools/preview-blocks.js.map +1 -1
  208. package/sidecar/dist/mcp/tools/query-logs.js.map +1 -1
  209. package/sidecar/dist/mcp/tools/typecheck.js.map +1 -1
  210. package/sidecar/dist/metrics/aggregate.js.map +1 -1
  211. package/sidecar/dist/metrics/types.js.map +1 -1
  212. package/sidecar/dist/pages/registry-client.js +14 -5
  213. package/sidecar/dist/pages/registry-client.js.map +1 -1
  214. package/sidecar/dist/pages/route-match.js.map +1 -1
  215. package/sidecar/dist/protocol/events.d.ts +7 -7
  216. package/sidecar/dist/protocol/events.js.map +1 -1
  217. package/sidecar/dist/protocol/json.d.ts +5 -0
  218. package/sidecar/dist/protocol/json.js +3 -0
  219. package/sidecar/dist/protocol/json.js.map +1 -0
  220. package/sidecar/dist/protocol/messages.d.ts +4 -4
  221. package/sidecar/dist/protocol/messages.js +3 -3
  222. package/sidecar/dist/protocol/messages.js.map +1 -1
  223. package/sidecar/dist/providers/claude/activity.d.ts +4 -2
  224. package/sidecar/dist/providers/claude/activity.js +29 -38
  225. package/sidecar/dist/providers/claude/activity.js.map +1 -1
  226. package/sidecar/dist/providers/claude/adapter.js +62 -91
  227. package/sidecar/dist/providers/claude/adapter.js.map +1 -1
  228. package/sidecar/dist/providers/claude/attachments.js +4 -4
  229. package/sidecar/dist/providers/claude/attachments.js.map +1 -1
  230. package/sidecar/dist/providers/claude/config.js.map +1 -1
  231. package/sidecar/dist/providers/claude/module.js.map +1 -1
  232. package/sidecar/dist/providers/claude/provider.d.ts +4 -2
  233. package/sidecar/dist/providers/claude/provider.js.map +1 -1
  234. package/sidecar/dist/providers/claude/resolve-binary.js +6 -4
  235. package/sidecar/dist/providers/claude/resolve-binary.js.map +1 -1
  236. package/sidecar/dist/providers/claude/safe-mode.js +14 -3
  237. package/sidecar/dist/providers/claude/safe-mode.js.map +1 -1
  238. package/sidecar/dist/providers/codex/adapter.js +124 -46
  239. package/sidecar/dist/providers/codex/adapter.js.map +1 -1
  240. package/sidecar/dist/providers/codex/attachments.js +1 -1
  241. package/sidecar/dist/providers/codex/attachments.js.map +1 -1
  242. package/sidecar/dist/providers/codex/chatgpt/credentials.js.map +1 -1
  243. package/sidecar/dist/providers/codex/chatgpt/http.js +8 -5
  244. package/sidecar/dist/providers/codex/chatgpt/http.js.map +1 -1
  245. package/sidecar/dist/providers/codex/chatgpt/lock.js +3 -3
  246. package/sidecar/dist/providers/codex/chatgpt/lock.js.map +1 -1
  247. package/sidecar/dist/providers/codex/chatgpt/login.js.map +1 -1
  248. package/sidecar/dist/providers/codex/chatgpt/oauth.js +51 -8
  249. package/sidecar/dist/providers/codex/chatgpt/oauth.js.map +1 -1
  250. package/sidecar/dist/providers/codex/chatgpt/store.js +23 -23
  251. package/sidecar/dist/providers/codex/chatgpt/store.js.map +1 -1
  252. package/sidecar/dist/providers/codex/client.d.ts +4 -3
  253. package/sidecar/dist/providers/codex/client.js +10 -3
  254. package/sidecar/dist/providers/codex/client.js.map +1 -1
  255. package/sidecar/dist/providers/codex/config.js.map +1 -1
  256. package/sidecar/dist/providers/codex/module.js.map +1 -1
  257. package/sidecar/dist/providers/codex/permissions.d.ts +2 -2
  258. package/sidecar/dist/providers/codex/permissions.js +20 -6
  259. package/sidecar/dist/providers/codex/permissions.js.map +1 -1
  260. package/sidecar/dist/providers/codex/process.d.ts +3 -1
  261. package/sidecar/dist/providers/codex/process.js +35 -36
  262. package/sidecar/dist/providers/codex/process.js.map +1 -1
  263. package/sidecar/dist/providers/codex/provider.d.ts +2 -0
  264. package/sidecar/dist/providers/codex/provider.js +23 -7
  265. package/sidecar/dist/providers/codex/provider.js.map +1 -1
  266. package/sidecar/dist/providers/codex/resolve-binary.js +2 -2
  267. package/sidecar/dist/providers/codex/resolve-binary.js.map +1 -1
  268. package/sidecar/dist/providers/codex/stop.d.ts +6 -1
  269. package/sidecar/dist/providers/codex/stop.js +29 -22
  270. package/sidecar/dist/providers/codex/stop.js.map +1 -1
  271. package/sidecar/dist/providers/registry.js +4 -5
  272. package/sidecar/dist/providers/registry.js.map +1 -1
  273. package/sidecar/dist/server/chat-socket-registry.d.ts +6 -6
  274. package/sidecar/dist/server/chat-socket-registry.js.map +1 -1
  275. package/sidecar/dist/server/conversation-runs.d.ts +1 -1
  276. package/sidecar/dist/server/conversation-runs.js +4 -5
  277. package/sidecar/dist/server/conversation-runs.js.map +1 -1
  278. package/sidecar/dist/server/host-socket-registry.d.ts +4 -3
  279. package/sidecar/dist/server/host-socket-registry.js.map +1 -1
  280. package/sidecar/dist/server/http-io.d.ts +1 -1
  281. package/sidecar/dist/server/http-io.js +2 -3
  282. package/sidecar/dist/server/http-io.js.map +1 -1
  283. package/sidecar/dist/server/http-params.js.map +1 -1
  284. package/sidecar/dist/server/http-routes.d.ts +9 -2
  285. package/sidecar/dist/server/http-routes.js +15 -8
  286. package/sidecar/dist/server/http-routes.js.map +1 -1
  287. package/sidecar/dist/server/http.js +21 -10
  288. package/sidecar/dist/server/http.js.map +1 -1
  289. package/sidecar/dist/server/pending-queue.js +4 -1
  290. package/sidecar/dist/server/pending-queue.js.map +1 -1
  291. package/sidecar/dist/server/raw-data.js +2 -1
  292. package/sidecar/dist/server/raw-data.js.map +1 -1
  293. package/sidecar/dist/server/routing.js +1 -1
  294. package/sidecar/dist/server/routing.js.map +1 -1
  295. package/sidecar/dist/server/safety-net.js +20 -9
  296. package/sidecar/dist/server/safety-net.js.map +1 -1
  297. package/sidecar/dist/server/tool-calls.js +7 -5
  298. package/sidecar/dist/server/tool-calls.js.map +1 -1
  299. package/sidecar/dist/server/turns.js +2 -1
  300. package/sidecar/dist/server/turns.js.map +1 -1
  301. package/sidecar/dist/server/ws.js +2 -6
  302. package/sidecar/dist/server/ws.js.map +1 -1
  303. package/sidecar/dist/skills/build-catalog.js.map +1 -1
  304. package/sidecar/dist/skills/frontmatter.js.map +1 -1
  305. package/sidecar/dist/skills/plugin-wrapper.js.map +1 -1
  306. package/sidecar/dist/skills/resolve-sources.js.map +1 -1
  307. package/sidecar/dist/skills/skill-usage.js +18 -12
  308. package/sidecar/dist/skills/skill-usage.js.map +1 -1
  309. package/sidecar/dist/state/conversations.js.map +1 -1
  310. package/sidecar/dist/state/lock.js +6 -2
  311. package/sidecar/dist/state/lock.js.map +1 -1
  312. package/sidecar/dist/state/settings-store.d.ts +31 -1
  313. package/sidecar/dist/state/settings-store.js +23 -24
  314. package/sidecar/dist/state/settings-store.js.map +1 -1
  315. package/sidecar/dist/state/settings-types.js.map +1 -1
  316. package/sidecar/dist/state/sidecar-version.js +3 -1
  317. package/sidecar/dist/state/sidecar-version.js.map +1 -1
  318. package/sidecar/dist/state/store.js +1 -1
  319. package/sidecar/dist/state/store.js.map +1 -1
  320. package/sidecar/dist/state/types.js.map +1 -1
  321. package/sidecar/package.json +1 -7
  322. package/skills/dms-ai/cases.json +47 -0
  323. package/skills/dms-ai-code-mode/cases.json +38 -0
  324. package/skills/dms-builder-safe/cases.json +67 -0
  325. package/skills/dms-external-database/REFERENCE.md +34 -9
  326. package/skills/dms-external-database/SKILL.md +24 -11
  327. package/skills/dms-external-database/cases.json +47 -0
  328. package/frontend-vue/tests/i18n-panel.test.ts +0 -118
  329. package/frontend-vue/tests/i18n-views.test.ts +0 -63
  330. package/frontend-vue/tests/panel-header-height.test.ts +0 -40
@@ -0,0 +1,190 @@
1
+ // @vitest-environment jsdom
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
+ sentOfType,
11
+ unmountPanel,
12
+ } from "./support/panel-harness";
13
+ import {
14
+ COMMAND_REQUEST,
15
+ DELETE_REQUEST,
16
+ DIFF_REQUEST,
17
+ keydown,
18
+ } from "./support/panel-fixtures";
19
+
20
+ beforeEach(() => {
21
+ localStorage.setItem("dms-ai-conversation-id", CONVERSATION_ID);
22
+ });
23
+
24
+ afterEach(() => {
25
+ unmountPanel();
26
+ localStorage.clear();
27
+ vi.useRealTimers();
28
+ });
29
+
30
+ describe("the approvals dock", () => {
31
+ it("previews a diff with line numbers and allows a scope with ↵", async () => {
32
+ // The countdown reads the clock: frozen at the request's creation, it
33
+ // shows the full grace period however slow the run is.
34
+ vi.useFakeTimers({ toFake: ["Date"] });
35
+ vi.setSystemTime(DIFF_REQUEST.createdAtMs);
36
+ const harness = createHarness();
37
+ const panel = await openPanel(harness);
38
+ deliver(harness, DIFF_REQUEST);
39
+ deliver(harness, COMMAND_REQUEST);
40
+ await nextTick();
41
+ const dock = panel.find(".cb-approve");
42
+ expect(dock.attributes("role")).toBe("alertdialog");
43
+ expect(dock.text()).toContain("Needs your approval · 1 of 2");
44
+ expect(dock.text()).toContain("denies in 4:12");
45
+ expect(panel.find(".perm.is-focused .perm__title").text()).toBe(
46
+ "Edit customer.form.ts",
47
+ );
48
+ expect(panel.findAll(".ln").map((row) => row.text())).toEqual([
49
+ "@@ −42 +42 @@",
50
+ '42−label: "VAT",',
51
+ '42+label: "VAT number",',
52
+ ]);
53
+ expect(dock.text()).toContain("Every edit under crm/customers/");
54
+ await panel.findAll(".perm.is-focused .scope-opt input")[2].setValue(true);
55
+ keydown(dock.element, "Enter");
56
+ await nextTick();
57
+ expect(sentOfType(harness, "permission_response")[0]).toEqual({
58
+ type: "permission_response",
59
+ conversationId: CONVERSATION_ID,
60
+ requestId: "r1",
61
+ decision: "allow_rule",
62
+ rule: { kind: "directory", value: "crm/customers/" },
63
+ });
64
+ expect(panel.find(".perm.is-focused .perm__title").text()).toBe(
65
+ "Run pnpm add jsvat",
66
+ );
67
+ expect(panel.find(".perm.is-focused").text()).toContain(
68
+ "adds a dependency · package.json, pnpm-lock.yaml",
69
+ );
70
+ });
71
+
72
+ it("moves with J and K, denies with N, and denies everything at once", async () => {
73
+ const harness = createHarness();
74
+ const panel = await openPanel(harness);
75
+ deliver(harness, DIFF_REQUEST);
76
+ deliver(harness, COMMAND_REQUEST);
77
+ await nextTick();
78
+ const dock = panel.find(".cb-approve");
79
+ keydown(dock.element, "j");
80
+ await nextTick();
81
+ expect(dock.text()).toContain("2 of 2");
82
+ keydown(dock.element, "k");
83
+ await nextTick();
84
+ expect(dock.text()).toContain("1 of 2");
85
+ keydown(dock.element, "n");
86
+ await nextTick();
87
+ expect(sentOfType(harness, "permission_response")[0]).toMatchObject({
88
+ requestId: "r1",
89
+ decision: "deny",
90
+ });
91
+ deliver(harness, DIFF_REQUEST);
92
+ await nextTick();
93
+ await buttonWithText(
94
+ panel.find(".cb-approve"),
95
+ "Deny both and stop",
96
+ ).trigger("click");
97
+ expect(sentOfType(harness, "permission_response").at(-1)).toMatchObject({
98
+ decision: "deny_all",
99
+ });
100
+ });
101
+
102
+ it("denies with the user's suggestion", async () => {
103
+ const harness = createHarness();
104
+ const panel = await openPanel(harness);
105
+ deliver(harness, DIFF_REQUEST);
106
+ await nextTick();
107
+ await buttonWithText(panel, "Suggest a change").trigger("click");
108
+ await panel
109
+ .find(".u-textarea")
110
+ .setValue("Use the Builder's validation instead");
111
+ await buttonWithText(panel, "Deny and send").trigger("click");
112
+ expect(sentOfType(harness, "permission_response")[0]).toMatchObject({
113
+ decision: "deny",
114
+ feedback: "Use the Builder's validation instead",
115
+ });
116
+ });
117
+
118
+ it("makes a data loss typed, offers to keep the data, and never a chat-wide rule", async () => {
119
+ const harness = createHarness();
120
+ const panel = await openPanel(harness);
121
+ deliver(harness, DELETE_REQUEST);
122
+ await nextTick();
123
+ const card = panel.find(".perm.is-danger");
124
+ expect(card.text()).toContain("Data loss");
125
+ expect(card.text()).not.toContain("this chat");
126
+ const confirm = buttonWithText(card, "Delete");
127
+ expect(confirm.attributes("disabled")).toBeDefined();
128
+ await card.find(".perm__confirm input").setValue("legacy_coupons");
129
+ expect(
130
+ buttonWithText(card, "Delete").attributes("disabled"),
131
+ ).toBeUndefined();
132
+ await card.find('input[type="checkbox"]').setValue(true);
133
+ await buttonWithText(card, "Remove the code only").trigger("click");
134
+ expect(sentOfType(harness, "permission_response")[0]).toMatchObject({
135
+ decision: "allow_once",
136
+ keepData: true,
137
+ });
138
+ });
139
+
140
+ it("turns an expired request into a card that asks again", async () => {
141
+ const harness = createHarness();
142
+ const panel = await openPanel(harness);
143
+ deliver(harness, DIFF_REQUEST);
144
+ deliver(harness, {
145
+ type: "permission_expired",
146
+ requestId: "r1",
147
+ toolName: "Edit",
148
+ summary: "Edit customer.form.ts",
149
+ expiredAtMs: Date.now(),
150
+ });
151
+ await nextTick();
152
+ expect(panel.find(".cb-approve").exists()).toBe(false);
153
+ const card = panel.find(".cb-expired");
154
+ expect(card.text()).toContain("Edit file request expired");
155
+ await buttonWithText(card, "Ask again").trigger("click");
156
+ expect(sentOfType(harness, "user_message")[0].content).toContain(
157
+ "Edit customer.form.ts",
158
+ );
159
+ expect(panel.find(".cb-expired").exists()).toBe(false);
160
+ });
161
+
162
+ it("lists the rules allowed in this chat and revokes one", async () => {
163
+ const harness = createHarness();
164
+ const panel = await openPanel(harness);
165
+ deliver(harness, DIFF_REQUEST);
166
+ deliver(harness, {
167
+ type: "rules_state",
168
+ rules: [
169
+ {
170
+ id: "rule-1",
171
+ kind: "command",
172
+ value: "pnpm test",
173
+ label: "Commands starting pnpm test",
174
+ createdAtMs: 1,
175
+ },
176
+ ],
177
+ });
178
+ await nextTick();
179
+ const rules = panel.find(".cb-approve .cb-rules");
180
+ expect(rules.text()).toContain("Commands starting pnpm test");
181
+ await buttonWithText(rules, "Revoke").trigger("click");
182
+ expect(sentOfType(harness, "revoke_rule")).toEqual([
183
+ {
184
+ type: "revoke_rule",
185
+ conversationId: CONVERSATION_ID,
186
+ ruleId: "rule-1",
187
+ },
188
+ ]);
189
+ });
190
+ });
@@ -0,0 +1,169 @@
1
+ // @vitest-environment jsdom
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
+ import { keydown } from "./support/panel-fixtures";
15
+
16
+ beforeEach(() => {
17
+ localStorage.setItem("dms-ai-conversation-id", CONVERSATION_ID);
18
+ });
19
+
20
+ afterEach(() => {
21
+ unmountPanel();
22
+ localStorage.clear();
23
+ vi.useRealTimers();
24
+ });
25
+
26
+ describe("questions, queue and modes", () => {
27
+ const QUESTIONS = {
28
+ type: "ask_question",
29
+ requestId: "q1",
30
+ questions: [
31
+ {
32
+ question: "Where should the page live?",
33
+ header: "Placement",
34
+ options: [
35
+ {
36
+ label: "Finance › Overdue invoices",
37
+ description: "Next to Invoices",
38
+ },
39
+ { label: "A tab on Invoices", description: "" },
40
+ ],
41
+ },
42
+ {
43
+ question: "Default sort?",
44
+ header: "Sort",
45
+ options: [
46
+ { label: "Days overdue", description: "" },
47
+ { label: "Amount", description: "" },
48
+ ],
49
+ },
50
+ ],
51
+ };
52
+
53
+ it("pages through questions with numbers, Other… and Skip, then keeps the answers", async () => {
54
+ const harness = createHarness();
55
+ const panel = await openPanel(harness);
56
+ deliver(harness, QUESTIONS);
57
+ await nextTick();
58
+ const dock = panel.find(".cb-ask");
59
+ expect(dock.text()).toContain("Question 1 of 2 · Placement");
60
+ expect(dock.find(".opt-card.is-selected").text()).toContain("Suggested");
61
+ keydown(dock.element, "3");
62
+ await nextTick();
63
+ await dock.find(".cb-ask__custom").setValue("Under Finance, as a tab");
64
+ await buttonWithText(dock.find(".cb-ask__foot"), "Next").trigger("click");
65
+ expect(dock.text()).toContain("Question 2 of 2 · Sort");
66
+ await buttonWithText(dock, "Skip, you decide").trigger("click");
67
+ expect(sentOfType(harness, "question_response")[0]).toEqual({
68
+ type: "question_response",
69
+ conversationId: CONVERSATION_ID,
70
+ requestId: "q1",
71
+ answers: ["Under Finance, as a tab", ""],
72
+ skipped: [false, true],
73
+ });
74
+ await nextTick();
75
+ const record = panel.find(".cb-answers");
76
+ expect(record.text()).toContain("“Under Finance, as a tab”");
77
+ expect(record.text()).toContain("Skipped: the assistant decides");
78
+ });
79
+
80
+ it("edits, reorders and clears the queue", async () => {
81
+ const harness = createHarness();
82
+ const panel = await openPanel(harness);
83
+ deliver(harness, {
84
+ type: "queue_state",
85
+ items: [
86
+ { id: "a", content: "Sort by order count" },
87
+ { id: "b", content: "Limit to 3 orders" },
88
+ ],
89
+ });
90
+ await nextTick();
91
+ await panel
92
+ .find('button[aria-label="Edit queued message"]')
93
+ .trigger("click");
94
+ await panel.find(".cb-queued__editor").setValue("Sort by revenue");
95
+ await buttonWithText(panel.find(".cb-queue"), "Save").trigger("click");
96
+ keydown(panel.findAll(".cb-queued__handle")[1].element, "ArrowUp");
97
+ await nextTick();
98
+ expect(panel.findAll(".cb-queued__txt").map((item) => item.text())).toEqual(
99
+ ["Limit to 3 orders", "Sort by revenue"],
100
+ );
101
+ await buttonWithText(panel.find(".cb-queue"), "Clear").trigger("click");
102
+ expect(
103
+ harness.sent
104
+ .map((msg) => msg.type)
105
+ .filter((type) => String(type).startsWith("queue_")),
106
+ ).toEqual(["queue_update", "queue_move", "queue_clear"]);
107
+ });
108
+
109
+ it("switches the chat's mode and scope, and confirms Full auto with a duration", async () => {
110
+ const harness = createHarness();
111
+ const panel = await openPanel(harness);
112
+ await buttonWithText(panel, "Plan only").trigger("click");
113
+ await buttonWithText(panel, "Full auto…").trigger("click");
114
+ await nextTick();
115
+ const modal = panel.find(".u-modal");
116
+ expect(modal.text()).toContain("Turn on Full auto?");
117
+ await buttonWithText(modal, "30 minutes").trigger("click");
118
+ await buttonWithText(modal, "Turn on Full auto").trigger("click");
119
+ expect(sentOfType(harness, "set_conversation_mode")).toEqual([
120
+ {
121
+ type: "set_conversation_mode",
122
+ conversationId: CONVERSATION_ID,
123
+ mode: "plan",
124
+ },
125
+ {
126
+ type: "set_conversation_mode",
127
+ conversationId: CONVERSATION_ID,
128
+ fullAuto: { duration: "30m" },
129
+ },
130
+ ]);
131
+ await nextTick();
132
+ const banner = panel.find(".cb-banner-auto");
133
+ expect(banner.text()).toContain("Full auto · for 30 minutes");
134
+ await buttonWithText(banner, "Turn off").trigger("click");
135
+ expect(sentOfType(harness, "set_conversation_mode").at(-1)).toMatchObject({
136
+ fullAuto: null,
137
+ });
138
+ });
139
+
140
+ it("runs a proposed plan in Ask first", async () => {
141
+ const harness = createHarness();
142
+ const panel = await openPanel(harness);
143
+ await sendMessage(panel, "plan a customer health page");
144
+ deliver(harness, {
145
+ type: "tool_call_start",
146
+ callId: "p1",
147
+ toolName: "ExitPlanMode",
148
+ args: { plan: "1. Create the page\n2. Add a query" },
149
+ });
150
+ deliver(harness, {
151
+ type: "tool_call_end",
152
+ callId: "p1",
153
+ status: "success",
154
+ result: "",
155
+ outcome: "done",
156
+ });
157
+ deliver(harness, { type: "run_done" });
158
+ await nextTick();
159
+ const plan = panel.find(".cb-plan");
160
+ expect(plan.text()).toContain("Proposed plan · 2 steps");
161
+ await buttonWithText(plan, "Run with Ask first").trigger("click");
162
+ expect(sentOfType(harness, "set_conversation_mode")[0]).toMatchObject({
163
+ mode: "normal",
164
+ });
165
+ expect(sentOfType(harness, "user_message").at(-1)).toMatchObject({
166
+ content: "Go ahead with the plan.",
167
+ });
168
+ });
169
+ });