@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,98 @@
1
+ <script setup lang="ts">
2
+ import type { PendingUndo } from "../composables/useChangeSetActions";
3
+ import { useChatI18n } from "../composables/useChatI18n";
4
+
5
+ interface Props {
6
+ /** The undo waiting on the user; the modal is open while there is one. */
7
+ pending: PendingUndo | null;
8
+ }
9
+
10
+ interface Emits {
11
+ confirm: [includeLater: boolean];
12
+ cancel: [];
13
+ }
14
+
15
+ defineProps<Props>();
16
+ const emit = defineEmits<Emits>();
17
+ const { t } = useChatI18n();
18
+
19
+ function onOpenChange(isOpen: boolean): void {
20
+ if (!isOpen) emit("cancel");
21
+ }
22
+ </script>
23
+
24
+ <template>
25
+ <UModal
26
+ :open="pending !== null"
27
+ :title="t('dms_ai.panel.change.conflict_title')"
28
+ @update:open="onOpenChange"
29
+ >
30
+ <template #body>
31
+ <p class="chat-view-conflict">
32
+ {{
33
+ t("dms_ai.panel.change.conflict_text", {
34
+ number: pending?.number ?? 0,
35
+ })
36
+ }}
37
+ </p>
38
+ <ul class="chat-view-conflicts">
39
+ <li
40
+ v-for="conflict in pending?.conflicts ?? []"
41
+ :key="conflict.changeSetId"
42
+ >
43
+ <b>#{{ conflict.number }}</b>
44
+ {{ conflict.title }}
45
+ <span>{{ conflict.files.join(", ") }}</span>
46
+ </li>
47
+ </ul>
48
+ </template>
49
+ <template #footer>
50
+ <div class="chat-view-conflict-foot">
51
+ <UButton
52
+ color="neutral"
53
+ variant="ghost"
54
+ :label="t('dms_ai.panel.change.undo_only')"
55
+ @click="emit('confirm', false)"
56
+ />
57
+ <UButton
58
+ color="secondary"
59
+ :label="t('dms_ai.panel.change.undo_together')"
60
+ @click="emit('confirm', true)"
61
+ />
62
+ </div>
63
+ </template>
64
+ </UModal>
65
+ </template>
66
+
67
+ <style scoped>
68
+ .chat-view-conflict {
69
+ margin: 0 0 10px;
70
+ font-size: 13px;
71
+ line-height: 1.5;
72
+ color: var(--ui-text-muted);
73
+ }
74
+
75
+ .chat-view-conflicts {
76
+ display: grid;
77
+ gap: 6px;
78
+ margin: 0;
79
+ padding: 0;
80
+ list-style: none;
81
+ font-size: 12.5px;
82
+ }
83
+
84
+ .chat-view-conflicts span {
85
+ display: block;
86
+ font-family: var(--ai-font-mono, ui-monospace, monospace);
87
+ font-size: 11px;
88
+ color: var(--ui-text-muted);
89
+ overflow-wrap: anywhere;
90
+ }
91
+
92
+ .chat-view-conflict-foot {
93
+ display: flex;
94
+ justify-content: flex-end;
95
+ gap: 8px;
96
+ width: 100%;
97
+ }
98
+ </style>
@@ -20,7 +20,7 @@ interface UndoPreview {
20
20
  }
21
21
 
22
22
  /** An undo waiting on the user: later sets touch the same files. */
23
- interface PendingUndo {
23
+ export interface PendingUndo {
24
24
  changeSetId: string;
25
25
  number: number;
26
26
  conflicts: UndoConflict[];
@@ -26,6 +26,10 @@ import {
26
26
  paletteAssistant,
27
27
  paletteSource,
28
28
  } from "../runtime/assistant-commands";
29
+ import {
30
+ ASSISTANT_ACTIONS_KEY,
31
+ publicAssistantActions,
32
+ } from "../runtime/assistant-actions";
29
33
  import { installAssistantNotifications } from "../runtime/assistant-notifications";
30
34
  import {
31
35
  ASSISTANT_SESSION_KEY,
@@ -264,8 +268,8 @@ function createSession(parts: SessionParts): AssistantSession {
264
268
  intents.push({ kind: "open", conversationId });
265
269
  panel.open();
266
270
  },
267
- startConversation: (prompt) => {
268
- intents.push({ kind: "start", prompt });
271
+ startConversation: (prompt, mode) => {
272
+ intents.push({ kind: "start", prompt, mode });
269
273
  panel.open();
270
274
  },
271
275
  holdStream: parts.holds.hold,
@@ -502,6 +506,10 @@ export default defineDmsPlugin((context) => {
502
506
  i18n: createChatI18n(context.$i18n),
503
507
  };
504
508
  context.vueApp.provide(ASSISTANT_SESSION_KEY, provided.session);
509
+ context.vueApp.provide(
510
+ ASSISTANT_ACTIONS_KEY,
511
+ publicAssistantActions(provided.session),
512
+ );
505
513
  context.vueApp.provide(CHAT_I18N_KEY, provided.i18n);
506
514
  const appMounted = new Promise<void>((resolve) =>
507
515
  context.hook("app:mounted", resolve),
@@ -0,0 +1,41 @@
1
+ import type {
2
+ AssistantSessionRef,
3
+ ConversationStartMode,
4
+ } from "./assistant-session";
5
+
6
+ /**
7
+ * The assistant as another module's layer may use it, without depending on dms-ai: injected under
8
+ * {@link ASSISTANT_ACTIONS_KEY} with a `null` default, which is what a project without dms-ai, or
9
+ * outside development, answers.
10
+ */
11
+ export interface PublicAssistantActions {
12
+ /** The assistant runs in this tab, so a button that starts a conversation can be offered. Reactive. */
13
+ readonly isAvailable: boolean;
14
+ /**
15
+ * Opens the assistant panel on a new conversation with `prompt` in the composer, unsent. With
16
+ * `mode`, the conversation is set to it first: `plan` makes the agent end with a plan to approve.
17
+ */
18
+ startConversation: (
19
+ prompt: string,
20
+ options?: { mode?: ConversationStartMode },
21
+ ) => void;
22
+ }
23
+
24
+ /**
25
+ * The global key of {@link PublicAssistantActions}: `Symbol.for` resolves to the same symbol in
26
+ * every layer, so another module's layer injects it without importing dms-ai.
27
+ */
28
+ export const ASSISTANT_ACTIONS_KEY = Symbol.for("dms-ai:assistant-actions");
29
+
30
+ /** The public actions over the tab's assistant session, which may not run yet, or ever. */
31
+ export function publicAssistantActions(
32
+ session: AssistantSessionRef,
33
+ ): PublicAssistantActions {
34
+ return {
35
+ get isAvailable() {
36
+ return session.value !== null;
37
+ },
38
+ startConversation: (prompt, options) =>
39
+ session.value?.startConversation(prompt, options?.mode),
40
+ };
41
+ }
@@ -17,6 +17,9 @@ export interface ChatPanelState {
17
17
  toggle: () => void;
18
18
  }
19
19
 
20
+ /** The approval modes a conversation can be started in from outside the panel. */
21
+ export type ConversationStartMode = "plan" | "normal";
22
+
20
23
  /**
21
24
  * The tab's assistant, as the plugin that owns it hands it to the panel, the
22
25
  * command palette's answer and the workspace pages: the sidecar's
@@ -31,8 +34,11 @@ export interface AssistantSession {
31
34
  navigate: (path: string) => void;
32
35
  /** Opens the panel on that conversation. */
33
36
  openConversation: (conversationId: string) => void;
34
- /** Opens the panel on a new conversation, the composer optionally prefilled. */
35
- startConversation: (prompt?: string) => void;
37
+ /**
38
+ * Opens the panel on a new conversation, the composer optionally prefilled, and the
39
+ * conversation set to `mode` first when one is given.
40
+ */
41
+ startConversation: (prompt?: string, mode?: ConversationStartMode) => void;
36
42
  /**
37
43
  * Keeps the tab's stream open while the panel is closed, until the returned
38
44
  * function releases it: the command palette's answer streams through it.
@@ -1,4 +1,5 @@
1
1
  import { type Ref, ref } from "vue";
2
+ import type { ConversationStartMode } from "./assistant-session";
2
3
 
3
4
  /** Opens the panel on a stored conversation. */
4
5
  interface OpenConversationIntent {
@@ -6,10 +7,11 @@ interface OpenConversationIntent {
6
7
  conversationId: string;
7
8
  }
8
9
 
9
- /** Opens the panel on a new conversation, the composer optionally prefilled. */
10
+ /** Opens the panel on a new conversation, the composer optionally prefilled, in `mode` when given. */
10
11
  interface StartConversationIntent {
11
12
  kind: "start";
12
13
  prompt?: string;
14
+ mode?: ConversationStartMode;
13
15
  }
14
16
 
15
17
  /** Puts the caret in the composer of the current conversation. */
@@ -0,0 +1,89 @@
1
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, relative, sep } from "node:path";
3
+ import type { Plugin } from "vite";
4
+
5
+ const BACKEND_PACKAGE = "@antelopejs/dms-ai";
6
+ const BACKEND_SOURCE = "src";
7
+ const QUERY_OR_HASH = /[?#].*$/;
8
+
9
+ const backendRoots = new Map<string, string | null>();
10
+
11
+ function packageName(manifest: string): string | undefined {
12
+ try {
13
+ const parsed: { name?: string } = JSON.parse(
14
+ readFileSync(manifest, "utf8"),
15
+ );
16
+ return parsed.name;
17
+ } catch {
18
+ return undefined;
19
+ }
20
+ }
21
+
22
+ function backendRoot(directory: string): string | null {
23
+ const known = backendRoots.get(directory);
24
+ if (known !== undefined) return known;
25
+ const manifest = join(directory, "package.json");
26
+ const parent = dirname(directory);
27
+ let root: string | null = null;
28
+ if (existsSync(manifest)) {
29
+ root = packageName(manifest) === BACKEND_PACKAGE ? directory : null;
30
+ } else if (parent !== directory) {
31
+ root = backendRoot(parent);
32
+ }
33
+ backendRoots.set(directory, root);
34
+ return root;
35
+ }
36
+
37
+ /**
38
+ * The real path of a resolved module id when it lies under the `src`
39
+ * directory of the backend package, or `undefined`. The package that owns the
40
+ * file decides, not a path, so the answer is the same in the repository and in
41
+ * the workspace the DMS builds the frontend in. Virtual ids and ids that are
42
+ * not files are ignored.
43
+ */
44
+ function backendSourceFile(id: string): string | undefined {
45
+ const path = id.replace(QUERY_OR_HASH, "");
46
+ if (!isAbsolute(path)) return undefined;
47
+ let real: string;
48
+ try {
49
+ real = realpathSync(path);
50
+ } catch {
51
+ return undefined;
52
+ }
53
+ const root = backendRoot(dirname(real));
54
+ if (root === null) return undefined;
55
+ return relative(root, real).split(sep)[0] === BACKEND_SOURCE
56
+ ? real
57
+ : undefined;
58
+ }
59
+
60
+ /**
61
+ * Fails the build, the dev server and the tests when a module of the frontend
62
+ * resolves to a file under the backend's `src`: the browser reaches the
63
+ * backend only through its HTTP routes. It checks what the whole resolution
64
+ * chain returns, so an alias is seen through and a symlink the resolver
65
+ * returns is followed to its real path, and it runs before the other plugins
66
+ * (`enforce: "pre"`).
67
+ */
68
+ export function backendBoundary(): Plugin {
69
+ return {
70
+ name: "dms-ai:backend-boundary",
71
+ enforce: "pre",
72
+ async resolveId(source, importer, options) {
73
+ const resolved = await this.resolve(source, importer, {
74
+ ...options,
75
+ skipSelf: true,
76
+ });
77
+ if (resolved === null || resolved.external) return resolved;
78
+ const file = backendSourceFile(resolved.id);
79
+ if (file === undefined) return resolved;
80
+ const from =
81
+ importer === undefined
82
+ ? "an entry point"
83
+ : importer.replace(QUERY_OR_HASH, "");
84
+ return this.error(
85
+ `Backend boundary: ${from} imports ${file}, which is under src of ${BACKEND_PACKAGE}. The frontend reaches the backend only through its HTTP routes.`,
86
+ );
87
+ },
88
+ };
89
+ }
@@ -0,0 +1,6 @@
1
+ import { defineDmsFrontendBuild } from "#dms/frontend-build";
2
+ import { backendBoundary } from "./build/backend-boundary.ts";
3
+
4
+ export default defineDmsFrontendBuild((build) => {
5
+ build.registerVitePlugins(() => [backendBoundary()]);
6
+ });
@@ -6,7 +6,7 @@
6
6
  "license": "Apache-2.0",
7
7
  "packageManager": "pnpm@10.6.5",
8
8
  "engines": {
9
- "@antelopejs/dms-frontend": ">=0.8.0 <0.9.0"
9
+ "@antelopejs/dms-frontend": ">=0.9.1 <0.10.0"
10
10
  },
11
11
  "scripts": {
12
12
  "build": "pnpm --dir \"${DMS_FRONTEND_WORKSPACE:?Set DMS_FRONTEND_WORKSPACE to the generated Inertia workspace}\" build",
@@ -37,6 +37,7 @@
37
37
  "prettier-plugin-tailwindcss": "^0.6.11",
38
38
  "typescript": "^5.8.2",
39
39
  "typescript-eslint": "^8.0.0",
40
+ "vite": "^7.3.2",
40
41
  "vitest": "^4.1.5",
41
42
  "vue-tsc": "^3.1.1"
42
43
  }
@@ -60,6 +60,9 @@ importers:
60
60
  typescript-eslint:
61
61
  specifier: ^8.0.0
62
62
  version: 8.70.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)
63
+ vite:
64
+ specifier: ^7.3.2
65
+ version: 7.3.2(jiti@2.7.0)(terser@5.46.2)(yaml@2.8.4)
63
66
  vitest:
64
67
  specifier: ^4.1.5
65
68
  version: 4.1.5(jsdom@29.1.1)(vite@7.3.2(jiti@2.7.0)(terser@5.46.2)(yaml@2.8.4))
@@ -0,0 +1,72 @@
1
+ // @vitest-environment jsdom
2
+ import { flushPromises } from "@vue/test-utils";
3
+ import { afterEach, describe, expect, it } from "vitest";
4
+ import { shallowRef } from "vue";
5
+ import { publicAssistantActions } from "../app/runtime/assistant-actions";
6
+ import {
7
+ createHarness,
8
+ openPanel,
9
+ sentOfType,
10
+ unmountPanel,
11
+ } from "./support/panel-harness";
12
+
13
+ afterEach(() => {
14
+ unmountPanel();
15
+ });
16
+
17
+ const PROMPT =
18
+ "Use dms-external-database on connection roaster, module builder-db-roaster-tables.";
19
+
20
+ describe("the assistant actions another module's layer injects", () => {
21
+ it("start a new conversation with the composer prefilled and nothing sent", async () => {
22
+ const harness = createHarness();
23
+ const panel = await openPanel(harness);
24
+ const actions = publicAssistantActions(shallowRef(harness.session));
25
+ expect(actions.isAvailable).toBe(true);
26
+ actions.startConversation(PROMPT);
27
+ await flushPromises();
28
+ expect((panel.find("textarea").element as HTMLTextAreaElement).value).toBe(
29
+ PROMPT,
30
+ );
31
+ expect(sentOfType(harness, "user_message")).toEqual([]);
32
+ expect(sentOfType(harness, "set_conversation_mode")).toEqual([]);
33
+ });
34
+
35
+ it("set the new conversation to plan mode before prefilling it, when asked", async () => {
36
+ const harness = createHarness();
37
+ const panel = await openPanel(harness);
38
+ const actions = publicAssistantActions(shallowRef(harness.session));
39
+ actions.startConversation(PROMPT, { mode: "plan" });
40
+ await flushPromises();
41
+ const started = sentOfType(harness, "hello").at(-1)?.conversationId;
42
+ expect(sentOfType(harness, "set_conversation_mode")).toEqual([
43
+ expect.objectContaining({ conversationId: started, mode: "plan" }),
44
+ ]);
45
+ expect((panel.find("textarea").element as HTMLTextAreaElement).value).toBe(
46
+ PROMPT,
47
+ );
48
+ });
49
+
50
+ it("set plan mode once the stream is up, when the panel opens before it", async () => {
51
+ const harness = createHarness();
52
+ await openPanel(harness);
53
+ harness.setChannelStatus("disconnected");
54
+ publicAssistantActions(shallowRef(harness.session)).startConversation(
55
+ PROMPT,
56
+ { mode: "plan" },
57
+ );
58
+ await flushPromises();
59
+ expect(sentOfType(harness, "set_conversation_mode")).toEqual([]);
60
+ harness.setChannelStatus("connected");
61
+ await flushPromises();
62
+ expect(sentOfType(harness, "set_conversation_mode")).toEqual([
63
+ expect.objectContaining({ mode: "plan" }),
64
+ ]);
65
+ });
66
+
67
+ it("are unavailable without a session, and starting then does nothing", () => {
68
+ const actions = publicAssistantActions(shallowRef(null));
69
+ expect(actions.isAvailable).toBe(false);
70
+ expect(() => actions.startConversation(PROMPT)).not.toThrow();
71
+ });
72
+ });
@@ -0,0 +1,130 @@
1
+ import {
2
+ mkdirSync,
3
+ mkdtempSync,
4
+ realpathSync,
5
+ rmSync,
6
+ symlinkSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { build, type Plugin } from "vite";
12
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
13
+ import { backendBoundary } from "../build/backend-boundary.ts";
14
+
15
+ let root: string;
16
+
17
+ function write(file: string, text: string): void {
18
+ const path = join(root, file);
19
+ mkdirSync(join(path, ".."), { recursive: true });
20
+ writeFileSync(path, text);
21
+ }
22
+
23
+ function manifest(directory: string, name: string): void {
24
+ write(`${directory}/package.json`, JSON.stringify({ name }));
25
+ }
26
+
27
+ /** A frontend entry with `source` as its only import, built for real. */
28
+ async function buildEntry(source: string, extra: Plugin[] = []): Promise<void> {
29
+ write(
30
+ "frontend/entry.ts",
31
+ `import "${source}";\nexport const frontend = 1;\n`,
32
+ );
33
+ await build({
34
+ root: join(root, "frontend"),
35
+ configFile: false,
36
+ logLevel: "silent",
37
+ plugins: [backendBoundary(), ...extra],
38
+ build: {
39
+ write: false,
40
+ lib: { entry: join(root, "frontend/entry.ts"), formats: ["es"] },
41
+ },
42
+ });
43
+ }
44
+
45
+ beforeEach(() => {
46
+ root = realpathSync(mkdtempSync(join(tmpdir(), "dms-ai-boundary-")));
47
+ manifest("backend", "@antelopejs/dms-ai");
48
+ write("backend/src/routes/status.ts", "export const status = 1;\n");
49
+ write("backend/dist/index.js", "export const built = 1;\n");
50
+ write("backend/srcfoo/index.ts", "export const sibling = 1;\n");
51
+ manifest("other", "another-package");
52
+ write("other/src/index.ts", "export const other = 1;\n");
53
+ manifest("frontend", "@antelopejs/dms-ai-frontend-vue");
54
+ });
55
+
56
+ afterEach(() => {
57
+ rmSync(root, { recursive: true, force: true });
58
+ });
59
+
60
+ describe("the backend boundary of a real build", () => {
61
+ it("fails when the frontend imports a file under the backend's src", async () => {
62
+ await expect(buildEntry("../backend/src/routes/status.ts")).rejects.toThrow(
63
+ /frontend\/entry\.ts imports .*backend\/src\/routes\/status\.ts/,
64
+ );
65
+ });
66
+
67
+ it("fails when the import carries a query", async () => {
68
+ await expect(
69
+ buildEntry("../backend/src/routes/status.ts?raw"),
70
+ ).rejects.toThrow(/Backend boundary/);
71
+ });
72
+
73
+ it("follows a symlink a resolver returns to the file it points at", async () => {
74
+ symlinkSync(
75
+ join(root, "backend/src/routes"),
76
+ join(root, "frontend/routes"),
77
+ );
78
+ const linked: Plugin = {
79
+ name: "test:linked",
80
+ enforce: "pre",
81
+ resolveId: (source) =>
82
+ source === "@linked/status"
83
+ ? join(root, "frontend/routes/status.ts")
84
+ : null,
85
+ };
86
+ await expect(buildEntry("@linked/status", [linked])).rejects.toThrow(
87
+ /backend\/src\/routes\/status\.ts/,
88
+ );
89
+ });
90
+
91
+ it("sees through an alias another plugin resolves", async () => {
92
+ const alias: Plugin = {
93
+ name: "test:alias",
94
+ enforce: "pre",
95
+ resolveId: (source) =>
96
+ source === "@backend/status"
97
+ ? join(root, "backend/src/routes/status.ts")
98
+ : null,
99
+ };
100
+ await expect(buildEntry("@backend/status", [alias])).rejects.toThrow(
101
+ /Backend boundary/,
102
+ );
103
+ });
104
+
105
+ it("lets the backend's built output through", async () => {
106
+ await expect(
107
+ buildEntry("../backend/dist/index.js"),
108
+ ).resolves.toBeUndefined();
109
+ });
110
+
111
+ it("lets a sibling directory whose name starts with src through", async () => {
112
+ await expect(
113
+ buildEntry("../backend/srcfoo/index.ts"),
114
+ ).resolves.toBeUndefined();
115
+ });
116
+
117
+ it("lets the src of another package through", async () => {
118
+ await expect(buildEntry("../other/src/index.ts")).resolves.toBeUndefined();
119
+ });
120
+
121
+ it("ignores a virtual module", async () => {
122
+ const virtual: Plugin = {
123
+ name: "test:virtual",
124
+ enforce: "pre",
125
+ resolveId: (source) => (source === "virtual:ok" ? "\0virtual:ok" : null),
126
+ load: (id) => (id === "\0virtual:ok" ? "export const v = 1;" : null),
127
+ };
128
+ await expect(buildEntry("virtual:ok", [virtual])).resolves.toBeUndefined();
129
+ });
130
+ });