@antelopejs/dms-ai 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/README.md +2 -1
  2. package/dist/lifecycle/skill-sources.js +19 -146
  3. package/dist/lifecycle/skill-sources.js.map +1 -1
  4. package/dist/pages/settings/availability.js +8 -1
  5. package/dist/pages/settings/availability.js.map +1 -1
  6. package/dist/pages/settings/options.d.ts +2 -1
  7. package/dist/pages/settings/options.js +29 -8
  8. package/dist/pages/settings/options.js.map +1 -1
  9. package/dist/pages/settings/sections.js +25 -4
  10. package/dist/pages/settings/sections.js.map +1 -1
  11. package/dist/routes/builder.d.ts +3 -3
  12. package/dist/routes/builder.js +10 -2
  13. package/dist/routes/builder.js.map +1 -1
  14. package/dist/routes/settings.js +1 -1
  15. package/dist/routes/settings.js.map +1 -1
  16. package/dist/sidecar/settings-values.d.ts +3 -0
  17. package/dist/sidecar/settings-values.js +33 -1
  18. package/dist/sidecar/settings-values.js.map +1 -1
  19. package/dist/vocabulary/enumerations.js +2 -0
  20. package/dist/vocabulary/enumerations.js.map +1 -1
  21. package/dist/vocabulary/tools.js +3 -0
  22. package/dist/vocabulary/tools.js.map +1 -1
  23. package/frontend-vue/app/chat/components/ChatView.vue +53 -85
  24. package/frontend-vue/app/chat/components/Composer.vue +23 -39
  25. package/frontend-vue/app/chat/components/NoticeCard.vue +25 -0
  26. package/frontend-vue/app/chat/components/PanelHeader.vue +2 -6
  27. package/frontend-vue/app/chat/components/ProviderNotice.vue +14 -3
  28. package/frontend-vue/app/chat/composables/conversation-events.ts +4 -3
  29. package/frontend-vue/app/chat/composables/useConversation.ts +6 -0
  30. package/frontend-vue/app/chat/composables/useConversationMode.ts +8 -11
  31. package/frontend-vue/app/chat/composables/useFollowScroll.ts +77 -0
  32. package/frontend-vue/app/chat/constants/modes.ts +15 -0
  33. package/frontend-vue/app/chat/constants/protocol.ts +0 -1
  34. package/frontend-vue/app/chat/constants/settings.ts +1 -1
  35. package/frontend-vue/app/chat/types/protocol.ts +19 -9
  36. package/frontend-vue/app/chat/types/settings.ts +7 -2
  37. package/frontend-vue/app/chat/utils/permission-view.ts +1 -0
  38. package/frontend-vue/app/chat/utils/status-label.ts +50 -0
  39. package/frontend-vue/app/chat/utils/tool-lexicon.ts +16 -0
  40. package/frontend-vue/app/plugins/ai.client.ts +2 -2
  41. package/frontend-vue/app/runtime/assistant-actions.ts +4 -2
  42. package/frontend-vue/app/runtime/assistant-session.ts +5 -1
  43. package/frontend-vue/app/runtime/block-health.ts +135 -0
  44. package/frontend-vue/app/runtime/constants.ts +1 -0
  45. package/frontend-vue/app/runtime/navigation-complete.ts +47 -6
  46. package/frontend-vue/app/runtime/panel-intents.ts +6 -2
  47. package/frontend-vue/app/views/constants.ts +2 -0
  48. package/frontend-vue/app/views/types.ts +3 -1
  49. package/frontend-vue/i18n/locales/dms-ai-pages-en-GB.json +28 -11
  50. package/frontend-vue/i18n/locales/dms-ai-pages-fr-FR.json +28 -11
  51. package/frontend-vue/i18n/locales/dms-ai-panel-en-GB.json +17 -22
  52. package/frontend-vue/i18n/locales/dms-ai-panel-fr-FR.json +17 -22
  53. package/frontend-vue/i18n/locales/dms-ai-views-en-GB.json +2 -0
  54. package/frontend-vue/i18n/locales/dms-ai-views-fr-FR.json +2 -0
  55. package/frontend-vue/tests/assistant-actions.test.ts +37 -0
  56. package/frontend-vue/tests/block-health.test.ts +142 -0
  57. package/frontend-vue/tests/chat/chat-scroll.test.ts +251 -0
  58. package/frontend-vue/tests/chat/conversation-events.test.ts +35 -3
  59. package/frontend-vue/tests/chat/provider-notice.test.ts +33 -0
  60. package/frontend-vue/tests/chat/tool-lexicon.test.ts +1 -0
  61. package/frontend-vue/tests/chat-panel.test.ts +2 -4
  62. package/frontend-vue/tests/navigation.test.ts +67 -2
  63. package/frontend-vue/tests/panel-modes.test.ts +146 -0
  64. package/frontend-vue/tests/panel-questions.test.ts +1 -19
  65. package/frontend-vue/tests/panel-states.test.ts +3 -15
  66. package/frontend-vue/tests/support/fake-layout.ts +119 -0
  67. package/frontend-vue/tests/support/panel-harness.ts +16 -8
  68. package/frontend-vue/tests/support/setup-layout.ts +3 -0
  69. package/frontend-vue/tests/support/stubs.ts +2 -0
  70. package/frontend-vue/vitest.config.ts +1 -0
  71. package/package.json +6 -5
  72. package/sidecar/dist/agent/always-ask-files.d.ts +2 -0
  73. package/sidecar/dist/agent/always-ask-files.js +34 -0
  74. package/sidecar/dist/agent/always-ask-files.js.map +1 -0
  75. package/sidecar/dist/agent/conversation-modes.d.ts +3 -12
  76. package/sidecar/dist/agent/conversation-modes.js +16 -69
  77. package/sidecar/dist/agent/conversation-modes.js.map +1 -1
  78. package/sidecar/dist/agent/effective-mode.d.ts +2 -5
  79. package/sidecar/dist/agent/effective-mode.js +1 -15
  80. package/sidecar/dist/agent/effective-mode.js.map +1 -1
  81. package/sidecar/dist/agent/permission-bus.d.ts +1 -2
  82. package/sidecar/dist/agent/permission-bus.js +1 -3
  83. package/sidecar/dist/agent/permission-bus.js.map +1 -1
  84. package/sidecar/dist/agent/permission-preview.d.ts +6 -0
  85. package/sidecar/dist/agent/permission-preview.js +35 -10
  86. package/sidecar/dist/agent/permission-preview.js.map +1 -1
  87. package/sidecar/dist/agent/permission-rules.js +2 -2
  88. package/sidecar/dist/agent/permission-rules.js.map +1 -1
  89. package/sidecar/dist/agent/project-secrets.d.ts +7 -0
  90. package/sidecar/dist/agent/project-secrets.js +61 -0
  91. package/sidecar/dist/agent/project-secrets.js.map +1 -0
  92. package/sidecar/dist/agent/provider.d.ts +10 -4
  93. package/sidecar/dist/agent/runner.d.ts +8 -2
  94. package/sidecar/dist/agent/runner.js +11 -3
  95. package/sidecar/dist/agent/runner.js.map +1 -1
  96. package/sidecar/dist/agent/session.d.ts +3 -3
  97. package/sidecar/dist/agent/session.js.map +1 -1
  98. package/sidecar/dist/agent/tool-kinds.d.ts +7 -1
  99. package/sidecar/dist/agent/tool-kinds.js +24 -5
  100. package/sidecar/dist/agent/tool-kinds.js.map +1 -1
  101. package/sidecar/dist/audit/activity-query.js +2 -0
  102. package/sidecar/dist/audit/activity-query.js.map +1 -1
  103. package/sidecar/dist/audit/activity.js +1 -0
  104. package/sidecar/dist/audit/activity.js.map +1 -1
  105. package/sidecar/dist/builder/builder-client.js +14 -2
  106. package/sidecar/dist/builder/builder-client.js.map +1 -1
  107. package/sidecar/dist/constants/audit.d.ts +1 -3
  108. package/sidecar/dist/constants/audit.js +2 -1
  109. package/sidecar/dist/constants/audit.js.map +1 -1
  110. package/sidecar/dist/constants/builder.d.ts +5 -1
  111. package/sidecar/dist/constants/builder.js +15 -0
  112. package/sidecar/dist/constants/builder.js.map +1 -1
  113. package/sidecar/dist/constants/claude.d.ts +8 -0
  114. package/sidecar/dist/constants/claude.js +8 -0
  115. package/sidecar/dist/constants/claude.js.map +1 -1
  116. package/sidecar/dist/constants/codex.d.ts +4 -0
  117. package/sidecar/dist/constants/codex.js +4 -0
  118. package/sidecar/dist/constants/codex.js.map +1 -1
  119. package/sidecar/dist/constants/mcp.d.ts +1 -1
  120. package/sidecar/dist/constants/mcp.js +1 -1
  121. package/sidecar/dist/constants/mcp.js.map +1 -1
  122. package/sidecar/dist/constants/native-binary.d.ts +5 -2
  123. package/sidecar/dist/constants/native-binary.js +5 -2
  124. package/sidecar/dist/constants/native-binary.js.map +1 -1
  125. package/sidecar/dist/constants/permissions.d.ts +2 -0
  126. package/sidecar/dist/constants/permissions.js +23 -0
  127. package/sidecar/dist/constants/permissions.js.map +1 -1
  128. package/sidecar/dist/constants/project-secrets.d.ts +5 -0
  129. package/sidecar/dist/constants/project-secrets.js +20 -0
  130. package/sidecar/dist/constants/project-secrets.js.map +1 -0
  131. package/sidecar/dist/constants/providers.d.ts +2 -0
  132. package/sidecar/dist/constants/providers.js +1 -0
  133. package/sidecar/dist/constants/providers.js.map +1 -1
  134. package/sidecar/dist/constants/settings.d.ts +2 -2
  135. package/sidecar/dist/constants/settings.js +4 -9
  136. package/sidecar/dist/constants/settings.js.map +1 -1
  137. package/sidecar/dist/constants/timing.d.ts +1 -0
  138. package/sidecar/dist/constants/timing.js +1 -0
  139. package/sidecar/dist/constants/timing.js.map +1 -1
  140. package/sidecar/dist/constants/tool-kinds.d.ts +67 -2
  141. package/sidecar/dist/constants/tool-kinds.js +111 -33
  142. package/sidecar/dist/constants/tool-kinds.js.map +1 -1
  143. package/sidecar/dist/mcp/tools/builder-compose.d.ts +51 -0
  144. package/sidecar/dist/mcp/tools/builder-compose.js +96 -0
  145. package/sidecar/dist/mcp/tools/builder-compose.js.map +1 -0
  146. package/sidecar/dist/mcp/tools/builder-gate.js +3 -1
  147. package/sidecar/dist/mcp/tools/builder-gate.js.map +1 -1
  148. package/sidecar/dist/mcp/tools/builder-project.js +119 -4
  149. package/sidecar/dist/mcp/tools/builder-project.js.map +1 -1
  150. package/sidecar/dist/mcp/tools/builder-queries.d.ts +31 -0
  151. package/sidecar/dist/mcp/tools/builder-queries.js +30 -13
  152. package/sidecar/dist/mcp/tools/builder-queries.js.map +1 -1
  153. package/sidecar/dist/mcp/tools/builder-table-view-schema.d.ts +99 -0
  154. package/sidecar/dist/mcp/tools/builder-table-view-schema.js +118 -0
  155. package/sidecar/dist/mcp/tools/builder-table-view-schema.js.map +1 -0
  156. package/sidecar/dist/mcp/tools/builder-table-view.d.ts +102 -0
  157. package/sidecar/dist/mcp/tools/builder-table-view.js +24 -0
  158. package/sidecar/dist/mcp/tools/builder-table-view.js.map +1 -0
  159. package/sidecar/dist/mcp/tools/builder.d.ts +135 -1
  160. package/sidecar/dist/mcp/tools/builder.js +17 -4
  161. package/sidecar/dist/mcp/tools/builder.js.map +1 -1
  162. package/sidecar/dist/mcp/tools/navigate-to-page.js +12 -4
  163. package/sidecar/dist/mcp/tools/navigate-to-page.js.map +1 -1
  164. package/sidecar/dist/pages/block-health.d.ts +14 -0
  165. package/sidecar/dist/pages/block-health.js +60 -0
  166. package/sidecar/dist/pages/block-health.js.map +1 -0
  167. package/sidecar/dist/prompts/agent.d.ts +1 -1
  168. package/sidecar/dist/prompts/agent.js +4 -3
  169. package/sidecar/dist/prompts/agent.js.map +1 -1
  170. package/sidecar/dist/protocol/events.d.ts +140 -63
  171. package/sidecar/dist/protocol/events.js +24 -8
  172. package/sidecar/dist/protocol/events.js.map +1 -1
  173. package/sidecar/dist/protocol/messages.d.ts +111 -49
  174. package/sidecar/dist/protocol/messages.js +30 -13
  175. package/sidecar/dist/protocol/messages.js.map +1 -1
  176. package/sidecar/dist/providers/claude/config.d.ts +19 -5
  177. package/sidecar/dist/providers/claude/config.js +21 -7
  178. package/sidecar/dist/providers/claude/config.js.map +1 -1
  179. package/sidecar/dist/providers/claude/model-fallback.d.ts +9 -0
  180. package/sidecar/dist/providers/claude/model-fallback.js +37 -0
  181. package/sidecar/dist/providers/claude/model-fallback.js.map +1 -0
  182. package/sidecar/dist/providers/claude/provider.js +35 -40
  183. package/sidecar/dist/providers/claude/provider.js.map +1 -1
  184. package/sidecar/dist/providers/claude/resolve-binary.js +18 -13
  185. package/sidecar/dist/providers/claude/resolve-binary.js.map +1 -1
  186. package/sidecar/dist/providers/claude/safe-mode.d.ts +5 -5
  187. package/sidecar/dist/providers/claude/safe-mode.js +49 -30
  188. package/sidecar/dist/providers/claude/safe-mode.js.map +1 -1
  189. package/sidecar/dist/providers/claude/secret-files.d.ts +2 -0
  190. package/sidecar/dist/providers/claude/secret-files.js +50 -0
  191. package/sidecar/dist/providers/claude/secret-files.js.map +1 -0
  192. package/sidecar/dist/providers/claude/session-stream.d.ts +18 -0
  193. package/sidecar/dist/providers/claude/session-stream.js +80 -0
  194. package/sidecar/dist/providers/claude/session-stream.js.map +1 -0
  195. package/sidecar/dist/providers/claude/tool-guard.d.ts +14 -0
  196. package/sidecar/dist/providers/claude/tool-guard.js +112 -0
  197. package/sidecar/dist/providers/claude/tool-guard.js.map +1 -0
  198. package/sidecar/dist/providers/codex/adapter.d.ts +10 -0
  199. package/sidecar/dist/providers/codex/adapter.js +20 -5
  200. package/sidecar/dist/providers/codex/adapter.js.map +1 -1
  201. package/sidecar/dist/providers/codex/config.d.ts +6 -17
  202. package/sidecar/dist/providers/codex/config.js +9 -56
  203. package/sidecar/dist/providers/codex/config.js.map +1 -1
  204. package/sidecar/dist/providers/codex/mcp-elicitation.d.ts +21 -0
  205. package/sidecar/dist/providers/codex/mcp-elicitation.js +66 -0
  206. package/sidecar/dist/providers/codex/mcp-elicitation.js.map +1 -0
  207. package/sidecar/dist/providers/codex/module.js +1 -1
  208. package/sidecar/dist/providers/codex/module.js.map +1 -1
  209. package/sidecar/dist/providers/codex/patch-directories.d.ts +6 -0
  210. package/sidecar/dist/providers/codex/patch-directories.js +53 -0
  211. package/sidecar/dist/providers/codex/patch-directories.js.map +1 -0
  212. package/sidecar/dist/providers/codex/permission-profiles.d.ts +11 -0
  213. package/sidecar/dist/providers/codex/permission-profiles.js +37 -0
  214. package/sidecar/dist/providers/codex/permission-profiles.js.map +1 -0
  215. package/sidecar/dist/providers/codex/permissions.d.ts +13 -4
  216. package/sidecar/dist/providers/codex/permissions.js +28 -2
  217. package/sidecar/dist/providers/codex/permissions.js.map +1 -1
  218. package/sidecar/dist/providers/codex/process.d.ts +1 -0
  219. package/sidecar/dist/providers/codex/process.js +3 -1
  220. package/sidecar/dist/providers/codex/process.js.map +1 -1
  221. package/sidecar/dist/providers/codex/protocol/common.d.ts +16 -4
  222. package/sidecar/dist/providers/codex/protocol/index.d.ts +2 -2
  223. package/sidecar/dist/providers/codex/protocol/index.js +1 -1
  224. package/sidecar/dist/providers/codex/protocol/index.js.map +1 -1
  225. package/sidecar/dist/providers/codex/protocol/v2.d.ts +23 -32
  226. package/sidecar/dist/providers/codex/provider.js +13 -12
  227. package/sidecar/dist/providers/codex/provider.js.map +1 -1
  228. package/sidecar/dist/providers/codex/secret-file-globs.d.ts +2 -0
  229. package/sidecar/dist/providers/codex/secret-file-globs.js +45 -0
  230. package/sidecar/dist/providers/codex/secret-file-globs.js.map +1 -0
  231. package/sidecar/dist/providers/types.d.ts +3 -1
  232. package/sidecar/dist/providers/types.js +2 -2
  233. package/sidecar/dist/providers/types.js.map +1 -1
  234. package/sidecar/dist/server/chat-events.js +1 -0
  235. package/sidecar/dist/server/chat-events.js.map +1 -1
  236. package/sidecar/dist/server/http-routes.js +1 -1
  237. package/sidecar/dist/server/http-routes.js.map +1 -1
  238. package/sidecar/dist/server/navigation-completer.d.ts +15 -2
  239. package/sidecar/dist/server/navigation-completer.js +42 -41
  240. package/sidecar/dist/server/navigation-completer.js.map +1 -1
  241. package/sidecar/dist/server/permission-events.d.ts +1 -1
  242. package/sidecar/dist/server/permission-events.js +5 -8
  243. package/sidecar/dist/server/permission-events.js.map +1 -1
  244. package/sidecar/dist/server/routing.js +11 -6
  245. package/sidecar/dist/server/routing.js.map +1 -1
  246. package/sidecar/dist/server/tool-calls.d.ts +0 -2
  247. package/sidecar/dist/server/tool-calls.js +7 -8
  248. package/sidecar/dist/server/tool-calls.js.map +1 -1
  249. package/sidecar/dist/server/turns.js +17 -1
  250. package/sidecar/dist/server/turns.js.map +1 -1
  251. package/sidecar/dist/server/ws.js +4 -15
  252. package/sidecar/dist/server/ws.js.map +1 -1
  253. package/sidecar/dist/skills/skill-usage.js +2 -2
  254. package/sidecar/dist/skills/skill-usage.js.map +1 -1
  255. package/sidecar/dist/state/conversations.d.ts +1 -0
  256. package/sidecar/dist/state/conversations.js +8 -0
  257. package/sidecar/dist/state/conversations.js.map +1 -1
  258. package/sidecar/dist/state/settings-store.d.ts +14 -5
  259. package/sidecar/dist/state/settings-store.js +34 -8
  260. package/sidecar/dist/state/settings-store.js.map +1 -1
  261. package/sidecar/dist/state/settings-types.d.ts +16 -2
  262. package/sidecar/dist/state/settings-types.js +14 -1
  263. package/sidecar/dist/state/settings-types.js.map +1 -1
  264. package/sidecar/dist/state/types.d.ts +14 -6
  265. package/sidecar/dist/state/types.js.map +1 -1
  266. package/sidecar/package.json +3 -3
  267. package/skills/dms-ai/SKILL.md +2 -1
  268. package/skills/dms-builder-safe/REFERENCE.md +5 -6
  269. package/skills/dms-builder-safe/SKILL.md +12 -10
  270. package/skills/dms-external-database/REFERENCE.md +20 -8
  271. package/skills/dms-external-database/SKILL.md +69 -20
  272. package/skills/dms-page-design/SKILL.md +192 -0
  273. package/skills/dms-page-design/cases.json +57 -0
  274. package/frontend-vue/app/chat/components/FullAutoBanner.vue +0 -76
  275. package/frontend-vue/app/chat/components/FullAutoDialog.vue +0 -103
@@ -263,13 +263,15 @@
263
263
  "approved": "Approved by you",
264
264
  "rule": "Allowed by a rule",
265
265
  "full_auto": "Full auto",
266
+ "auto_approved": "Allowed by Auto review",
267
+ "auto_denied": "Blocked by Auto review",
266
268
  "blocked": "Blocked · safe mode",
267
269
  "denied": "Denied by you",
268
270
  "expired": "Expired"
269
271
  },
270
272
  "allowed_metric": {
271
273
  "deletions_asked": "Deletions asked",
272
- "deletions_note": "Deletions always ask, even in Full auto"
274
+ "deletions_note": "Deletions always ask, in every mode"
273
275
  },
274
276
  "result": {
275
277
  "done": "Done",
@@ -314,6 +316,8 @@
314
316
  "BuilderPageStructure": "Read page",
315
317
  "BuilderCreatePage": "Create page",
316
318
  "BuilderConfigurePage": "Configure page",
319
+ "BuilderComposePage": "Compose page",
320
+ "BuilderConfigureTableView": "Configure table view",
317
321
  "BuilderDeletePage": "Delete page",
318
322
  "BuilderAddBlock": "Add block",
319
323
  "BuilderConfigureBlock": "Configure block",
@@ -332,6 +336,7 @@
332
336
  "BuilderRemoveField": "Remove field",
333
337
  "BuilderQueryTemplates": "Read query templates",
334
338
  "BuilderAddQuery": "Add query",
339
+ "BuilderPreviewQuery": "Preview query",
335
340
  "BuilderConfigureQuery": "Configure query",
336
341
  "BuilderRemoveQuery": "Remove query",
337
342
  "UndoChangeSet": "Undo change set",
@@ -418,6 +423,7 @@
418
423
  "label": "Coding agent",
419
424
  "description": "Runs on this machine, in the project folder. Your code only goes to the provider you pick; a failing agent never falls back to the other.",
420
425
  "unavailable": "Not set up on this machine",
426
+ "codex_not_signed_in": "Sign in with ChatGPT below to run Codex on your ChatGPT plan.",
421
427
  "claude": {
422
428
  "label": "Claude Code",
423
429
  "description": "Anthropic · model chosen by Claude Code"
@@ -462,29 +468,40 @@
462
468
  "high": "High"
463
469
  }
464
470
  },
471
+ "claude_model": {
472
+ "label": "Claude model",
473
+ "description": "The model Claude Code runs on. Thinking sets its reasoning effort."
474
+ },
475
+ "default_mode_claude": {
476
+ "label": "Default mode on Claude Code",
477
+ "description": "What Claude Code may do without asking you first. Auto review is the recommended default."
478
+ },
479
+ "default_mode_codex": {
480
+ "label": "Default mode on Codex",
481
+ "description": "What Codex may do without asking you first. Codex has no Auto review."
482
+ },
465
483
  "mode": {
466
- "label": "Approval mode",
467
- "description": "What the assistant may do without asking you first.",
468
484
  "normal": {
469
485
  "label": "Ask first",
470
- "description": "Asks before any change outside the Builder."
486
+ "description": "Asks before any change outside the Builder and before every command, read-only ones included."
487
+ },
488
+ "auto": {
489
+ "label": "Auto review",
490
+ "description": "A safety check approves routine file edits and web access. Shell commands, dependency files, git hooks and deletions still ask. Asks first on models or plans without it."
471
491
  },
472
492
  "acceptEdits": {
473
493
  "label": "Accept edits",
474
- "description": "Applies file edits, asks for the rest."
494
+ "description": "Applies file edits, except dependency and git hook files. Asks for the rest, every command included.",
495
+ "description_codex": "With Codex, works as Ask first: asks before every file edit, dependency and git hook files included, and before any command that writes."
475
496
  },
476
497
  "plan": {
477
498
  "label": "Plan only",
478
499
  "description": "Reads and proposes. Changes nothing."
479
- },
480
- "auto": {
481
- "label": "Full auto",
482
- "description": "Not available as a default. Turn it on per chat, for a limited time."
483
500
  }
484
501
  },
485
502
  "always_ask": {
486
503
  "label": "Always ask for",
487
- "description": "These ask in every mode, including Full auto."
504
+ "description": "These ask in every mode."
488
505
  },
489
506
  "always_ask_deletions": {
490
507
  "label": "Deleting pages, tables, fields or queries",
@@ -610,7 +627,7 @@
610
627
  "normal": "Ask first",
611
628
  "acceptEdits": "Accept edits",
612
629
  "plan": "Plan only",
613
- "auto": "Full auto"
630
+ "auto": "Auto review"
614
631
  },
615
632
  "facts": {
616
633
  "state": "Status",
@@ -263,13 +263,15 @@
263
263
  "approved": "Autorisée par vous",
264
264
  "rule": "Autorisée par une règle",
265
265
  "full_auto": "Automatique complet",
266
+ "auto_approved": "Autorisé par la revue automatique",
267
+ "auto_denied": "Bloqué par la revue automatique",
266
268
  "blocked": "Bloquée · mode sûr",
267
269
  "denied": "Refusée par vous",
268
270
  "expired": "Expirée"
269
271
  },
270
272
  "allowed_metric": {
271
273
  "deletions_asked": "Suppressions demandées",
272
- "deletions_note": "Les suppressions demandent toujours, même en automatique complet"
274
+ "deletions_note": "Les suppressions demandent toujours, dans tous les modes"
273
275
  },
274
276
  "result": {
275
277
  "done": "Terminée",
@@ -314,6 +316,8 @@
314
316
  "BuilderPageStructure": "Lire une page",
315
317
  "BuilderCreatePage": "Créer une page",
316
318
  "BuilderConfigurePage": "Configurer une page",
319
+ "BuilderComposePage": "Composer une page",
320
+ "BuilderConfigureTableView": "Configurer une vue tableau",
317
321
  "BuilderDeletePage": "Supprimer une page",
318
322
  "BuilderAddBlock": "Ajouter un bloc",
319
323
  "BuilderConfigureBlock": "Configurer un bloc",
@@ -332,6 +336,7 @@
332
336
  "BuilderRemoveField": "Retirer un champ",
333
337
  "BuilderQueryTemplates": "Lire les modèles de requêtes",
334
338
  "BuilderAddQuery": "Ajouter une requête",
339
+ "BuilderPreviewQuery": "Aperçu de la requête",
335
340
  "BuilderConfigureQuery": "Configurer une requête",
336
341
  "BuilderRemoveQuery": "Retirer une requête",
337
342
  "UndoChangeSet": "Annuler un lot de modifications",
@@ -418,6 +423,7 @@
418
423
  "label": "Agent de code",
419
424
  "description": "Tourne sur cette machine, dans le dossier du projet. Votre code ne part que chez le fournisseur choisi ; un agent en panne ne bascule jamais sur l'autre.",
420
425
  "unavailable": "Non configuré sur cette machine",
426
+ "codex_not_signed_in": "Connectez-vous avec ChatGPT ci-dessous pour utiliser Codex avec votre forfait ChatGPT.",
421
427
  "claude": {
422
428
  "label": "Claude Code",
423
429
  "description": "Anthropic · modèle choisi par Claude Code"
@@ -462,29 +468,40 @@
462
468
  "high": "Élevée"
463
469
  }
464
470
  },
471
+ "claude_model": {
472
+ "label": "Modèle Claude",
473
+ "description": "Le modèle sur lequel tourne Claude Code. La réflexion règle son niveau d'effort."
474
+ },
475
+ "default_mode_claude": {
476
+ "label": "Mode par défaut avec Claude Code",
477
+ "description": "Ce que Claude Code peut faire sans vous demander d'abord. La revue automatique est le choix recommandé."
478
+ },
479
+ "default_mode_codex": {
480
+ "label": "Mode par défaut avec Codex",
481
+ "description": "Ce que Codex peut faire sans vous demander d'abord. Codex n'a pas de revue automatique."
482
+ },
465
483
  "mode": {
466
- "label": "Mode de validation",
467
- "description": "Ce que l'assistant peut faire sans vous demander d'abord.",
468
484
  "normal": {
469
485
  "label": "Demander d'abord",
470
- "description": "Demande avant toute modification en dehors du Builder."
486
+ "description": "Demande avant toute modification en dehors du Builder et avant chaque commande, même en lecture seule."
487
+ },
488
+ "auto": {
489
+ "label": "Revue automatique",
490
+ "description": "Un contrôle de sécurité approuve les modifications de fichiers courantes et l'accès au web. Les commandes shell, les fichiers de dépendances, les hooks git et les suppressions demandent encore votre accord. Sur un modèle ou une offre qui ne la permet pas, l'assistant demande avant d'agir."
471
491
  },
472
492
  "acceptEdits": {
473
493
  "label": "Accepter les modifications",
474
- "description": "Applique les modifications de fichiers, demande pour le reste."
494
+ "description": "Applique les modifications de fichiers, sauf les fichiers de dépendances et les hooks git. Demande pour le reste, chaque commande comprise.",
495
+ "description_codex": "Avec Codex, fonctionne comme Demander d'abord : demande avant chaque modification de fichier, fichiers de dépendances et hooks git compris, et avant toute commande qui écrit."
475
496
  },
476
497
  "plan": {
477
498
  "label": "Plan seulement",
478
499
  "description": "Lit et propose. Ne modifie rien."
479
- },
480
- "auto": {
481
- "label": "Automatique complet",
482
- "description": "Indisponible par défaut. Activez-le par conversation, pour une durée limitée."
483
500
  }
484
501
  },
485
502
  "always_ask": {
486
503
  "label": "Toujours demander pour",
487
- "description": "Ces opérations demandent dans tous les modes, automatique complet compris."
504
+ "description": "Ces opérations demandent dans tous les modes."
488
505
  },
489
506
  "always_ask_deletions": {
490
507
  "label": "Supprimer des pages, tables, champs ou requêtes",
@@ -610,7 +627,7 @@
610
627
  "normal": "Demander d'abord",
611
628
  "acceptEdits": "Accepter les modifications",
612
629
  "plan": "Plan seulement",
613
- "auto": "Automatique complet"
630
+ "auto": "Revue automatique"
614
631
  },
615
632
  "facts": {
616
633
  "state": "État",
@@ -7,9 +7,9 @@
7
7
  },
8
8
  "mode": {
9
9
  "normal": "Ask first",
10
+ "auto": "Auto review",
10
11
  "acceptEdits": "Accept edits",
11
- "plan": "Plan only",
12
- "auto": "Full auto"
12
+ "plan": "Plan only"
13
13
  },
14
14
  "scope": {
15
15
  "safe": "Safe mode",
@@ -142,7 +142,11 @@
142
142
  "expired_title": "{tool} request expired.",
143
143
  "expired_text": "“{summary}” was not answered in time, so it was denied at {time} and the assistant was told why.",
144
144
  "question_skipped": "You let the assistant decide “{header}”.",
145
- "full_auto_ended": "Full auto ended at {time}. The assistant asks first again."
145
+ "full_auto_ended": "Full auto ended at {time}. The assistant asks first again.",
146
+ "auto_unavailable_title": "Auto review isn't available here.",
147
+ "auto_unavailable_text": "{model}, this plan or a setting doesn't allow it. This chat asks first instead.",
148
+ "model_fallback_title": "Switched to {model}.",
149
+ "model_fallback_text": "The requested model, {requested}, was refused (unknown, or no access on this plan). This answer comes from {model}, the default model."
146
150
  },
147
151
  "errors": {
148
152
  "turn_title": "The turn stopped.",
@@ -154,6 +158,7 @@
154
158
  "stop_not_delivered": "The assistant could not be reached to stop the run. Reconnect to see where it stands.",
155
159
  "agent_cant_run": "{agent} can't run.",
156
160
  "agent_no_fallback": "Your messages are not sent to any other agent.",
161
+ "codex_not_signed_in": "Sign in with ChatGPT in Assistant › Settings to run Codex on your ChatGPT plan.",
157
162
  "open_settings": "Open settings"
158
163
  },
159
164
  "plan": {
@@ -232,7 +237,8 @@
232
237
  "delete_table": "Delete the table {target}",
233
238
  "remove_field": "Remove the field {target}",
234
239
  "delete_category": "Delete the category {target}",
235
- "remove_query": "Remove the query {target}"
240
+ "remove_query": "Remove the query {target}",
241
+ "map_resources": "Map the tables and remove the pages {target}"
236
242
  },
237
243
  "consequence_deletes_data": "Removes the code and its data",
238
244
  "consequence_removes_code": "Removes the code · undoable"
@@ -288,12 +294,12 @@
288
294
  },
289
295
  "mode_menu": "Approval mode · this chat",
290
296
  "mode_desc": {
291
- "normal": "Asks before any edit, command or web request.",
292
- "acceptEdits": "Applies file edits, asks for commands and web requests.",
297
+ "normal": "Asks before any edit, web request or command, read-only commands included.",
298
+ "acceptEdits": "Applies file edits, except dependency and git hook files. Asks before every command and web request.",
299
+ "acceptEdits_codex": "With Codex, works as Ask first: asks before every file edit, dependency and git hook files included, and before any command that writes.",
293
300
  "plan": "Reads and proposes a plan. Changes nothing.",
294
- "auto": "Runs everything without asking, except deletions."
301
+ "auto": "A safety check approves routine file edits and web access. Shell commands, dependency files, git hooks and deletions still ask."
295
302
  },
296
- "full_auto_item": "Full auto…",
297
303
  "default_mode": "Default for new chats",
298
304
  "hint_send": "send",
299
305
  "hint_queue": "queue",
@@ -304,20 +310,6 @@
304
310
  "usage_new": "new chat",
305
311
  "usage_offline": "offline"
306
312
  },
307
- "full_auto": {
308
- "title": "Turn on Full auto?",
309
- "description": "The assistant will edit files and run commands in this chat without asking. Deletions and dependency changes still ask. Every change stays in the change set and can be undone.",
310
- "turn_off_after": "Turn off after",
311
- "duration_turn": "This turn",
312
- "duration_30m": "30 minutes",
313
- "duration_chat": "Closing the chat",
314
- "confirm": "Turn on Full auto",
315
- "banner_turn": "this turn only",
316
- "banner_30m": "for 30 minutes",
317
- "banner_chat": "until the chat closes",
318
- "banner_until": "ends in {time}",
319
- "turn_off": "Turn off"
320
- },
321
313
  "drawer": {
322
314
  "title": "Conversations",
323
315
  "new": "New",
@@ -430,6 +422,8 @@
430
422
  "configure_page": "Configure page",
431
423
  "delete_page": "Delete page",
432
424
  "add_block": "Add block",
425
+ "compose_page": "Compose page",
426
+ "configure_table_view": "Configure table view",
433
427
  "configure_block": "Configure block",
434
428
  "move_block": "Move block",
435
429
  "remove_block": "Remove block",
@@ -446,6 +440,7 @@
446
440
  "remove_field": "Remove field",
447
441
  "browse_queries": "Browse query templates",
448
442
  "add_query": "Add query",
443
+ "preview_query": "Preview query",
449
444
  "configure_query": "Configure query",
450
445
  "remove_query": "Remove query",
451
446
  "search_tools": "Load tools",
@@ -7,9 +7,9 @@
7
7
  },
8
8
  "mode": {
9
9
  "normal": "Demander d'abord",
10
+ "auto": "Revue automatique",
10
11
  "acceptEdits": "Accepter les modifications",
11
- "plan": "Plan seulement",
12
- "auto": "Automatique complet"
12
+ "plan": "Plan seulement"
13
13
  },
14
14
  "scope": {
15
15
  "safe": "Mode sûr",
@@ -142,7 +142,11 @@
142
142
  "expired_title": "Demande « {tool} » expirée.",
143
143
  "expired_text": "« {summary} » est restée sans réponse : elle a été refusée à {time} et l'assistant en a été informé.",
144
144
  "question_skipped": "Vous avez laissé l'assistant décider pour « {header} ».",
145
- "full_auto_ended": "Le mode automatique complet a pris fin à {time}. L'assistant redemande avant d'agir."
145
+ "full_auto_ended": "Le mode automatique complet a pris fin à {time}. L'assistant redemande avant d'agir.",
146
+ "auto_unavailable_title": "La revue automatique n'est pas disponible ici.",
147
+ "auto_unavailable_text": "{model}, votre offre ou un réglage ne la permet pas. Dans cette conversation, l'assistant demande avant d'agir.",
148
+ "model_fallback_title": "Passage à {model}.",
149
+ "model_fallback_text": "Le modèle demandé, {requested}, a été refusé (inconnu, ou sans accès avec cet abonnement). Cette réponse vient de {model}, le modèle par défaut."
146
150
  },
147
151
  "errors": {
148
152
  "turn_title": "Le tour s'est arrêté.",
@@ -154,6 +158,7 @@
154
158
  "stop_not_delivered": "L'assistant n'a pas pu être joint pour arrêter le tour. Reconnectez-vous pour voir où il en est.",
155
159
  "agent_cant_run": "{agent} ne peut pas démarrer.",
156
160
  "agent_no_fallback": "Vos messages ne sont envoyés à aucun autre agent.",
161
+ "codex_not_signed_in": "Connectez-vous avec ChatGPT dans Assistant › Réglages pour utiliser Codex avec votre forfait ChatGPT.",
157
162
  "open_settings": "Ouvrir les paramètres"
158
163
  },
159
164
  "plan": {
@@ -232,7 +237,8 @@
232
237
  "delete_table": "Supprimer la table {target}",
233
238
  "remove_field": "Retirer le champ {target}",
234
239
  "delete_category": "Supprimer la catégorie {target}",
235
- "remove_query": "Retirer la requête {target}"
240
+ "remove_query": "Retirer la requête {target}",
241
+ "map_resources": "Mapper les tables et retirer les pages {target}"
236
242
  },
237
243
  "consequence_deletes_data": "Retire le code et ses données",
238
244
  "consequence_removes_code": "Retire le code · annulable"
@@ -288,12 +294,12 @@
288
294
  },
289
295
  "mode_menu": "Mode d'approbation · cette conversation",
290
296
  "mode_desc": {
291
- "normal": "Demande avant toute modification, commande ou requête web.",
292
- "acceptEdits": "Applique les modifications de fichiers, demande pour les commandes et les requêtes web.",
297
+ "normal": "Demande avant toute modification, requête web ou commande, même en lecture seule.",
298
+ "acceptEdits": "Applique les modifications de fichiers, sauf les fichiers de dépendances et les hooks git. Demande avant chaque commande et requête web.",
299
+ "acceptEdits_codex": "Avec Codex, fonctionne comme Demander d'abord : demande avant chaque modification de fichier, fichiers de dépendances et hooks git compris, et avant toute commande qui écrit.",
293
300
  "plan": "Lit et propose un plan. Ne modifie rien.",
294
- "auto": "Exécute tout sans demander, sauf les suppressions."
301
+ "auto": "Un contrôle de sécurité approuve les modifications de fichiers courantes et l'accès au web. Les commandes shell, les fichiers de dépendances, les hooks git et les suppressions demandent encore votre accord."
295
302
  },
296
- "full_auto_item": "Automatique complet…",
297
303
  "default_mode": "Par défaut pour les nouvelles conversations",
298
304
  "hint_send": "envoyer",
299
305
  "hint_queue": "mettre en file",
@@ -304,20 +310,6 @@
304
310
  "usage_new": "nouvelle conversation",
305
311
  "usage_offline": "hors ligne"
306
312
  },
307
- "full_auto": {
308
- "title": "Activer le mode automatique complet ?",
309
- "description": "L'assistant modifiera des fichiers et exécutera des commandes dans cette conversation sans demander. Les suppressions et les changements de dépendances demandent toujours. Chaque modification reste dans le lot de modifications et peut être annulée.",
310
- "turn_off_after": "Désactiver après",
311
- "duration_turn": "Ce tour",
312
- "duration_30m": "30 minutes",
313
- "duration_chat": "Fermeture de la conversation",
314
- "confirm": "Activer le mode automatique complet",
315
- "banner_turn": "ce tour seulement",
316
- "banner_30m": "pendant 30 minutes",
317
- "banner_chat": "jusqu'à la fermeture de la conversation",
318
- "banner_until": "se termine dans {time}",
319
- "turn_off": "Désactiver"
320
- },
321
313
  "drawer": {
322
314
  "title": "Conversations",
323
315
  "new": "Nouvelle",
@@ -430,6 +422,8 @@
430
422
  "configure_page": "Configurer la page",
431
423
  "delete_page": "Supprimer la page",
432
424
  "add_block": "Ajouter un bloc",
425
+ "compose_page": "Composer une page",
426
+ "configure_table_view": "Configurer la vue de tableau",
433
427
  "configure_block": "Configurer le bloc",
434
428
  "move_block": "Déplacer le bloc",
435
429
  "remove_block": "Retirer le bloc",
@@ -446,6 +440,7 @@
446
440
  "remove_field": "Retirer le champ",
447
441
  "browse_queries": "Parcourir les modèles de requête",
448
442
  "add_query": "Ajouter une requête",
443
+ "preview_query": "Aperçu de la requête",
449
444
  "configure_query": "Configurer la requête",
450
445
  "remove_query": "Retirer la requête",
451
446
  "search_tools": "Charger des outils",
@@ -49,6 +49,8 @@
49
49
  "approved": "Approved by you",
50
50
  "rule": "Allowed by a rule",
51
51
  "full_auto": "Full auto",
52
+ "auto_approved": "Allowed by Auto review",
53
+ "auto_denied": "Blocked by Auto review",
52
54
  "blocked": "Blocked · safe mode",
53
55
  "denied": "Denied by you",
54
56
  "expired": "Expired, not answered"
@@ -49,6 +49,8 @@
49
49
  "approved": "Autorisé par vous",
50
50
  "rule": "Autorisé par une règle",
51
51
  "full_auto": "Automatique complet",
52
+ "auto_approved": "Autorisé par la revue automatique",
53
+ "auto_denied": "Bloqué par la revue automatique",
52
54
  "blocked": "Bloqué · mode sûr",
53
55
  "denied": "Refusé par vous",
54
56
  "expired": "Expiré, sans réponse"
@@ -64,6 +64,43 @@ describe("the assistant actions another module's layer injects", () => {
64
64
  ]);
65
65
  });
66
66
 
67
+ it("send the prompt as the first message, in plan mode, when asked to", async () => {
68
+ const harness = createHarness();
69
+ const panel = await openPanel(harness);
70
+ publicAssistantActions(shallowRef(harness.session)).startConversation(
71
+ PROMPT,
72
+ { mode: "plan", send: true },
73
+ );
74
+ await flushPromises();
75
+ const started = sentOfType(harness, "hello").at(-1)?.conversationId;
76
+ expect(sentOfType(harness, "set_conversation_mode")).toEqual([
77
+ expect.objectContaining({ conversationId: started, mode: "plan" }),
78
+ ]);
79
+ expect(sentOfType(harness, "user_message")).toEqual([
80
+ expect.objectContaining({ content: PROMPT }),
81
+ ]);
82
+ expect((panel.find("textarea").element as HTMLTextAreaElement).value).toBe(
83
+ "",
84
+ );
85
+ });
86
+
87
+ it("send the prompt once the stream is up, when the panel opens before it", async () => {
88
+ const harness = createHarness();
89
+ await openPanel(harness);
90
+ harness.setChannelStatus("disconnected");
91
+ publicAssistantActions(shallowRef(harness.session)).startConversation(
92
+ PROMPT,
93
+ { send: true },
94
+ );
95
+ await flushPromises();
96
+ expect(sentOfType(harness, "user_message")).toEqual([]);
97
+ harness.setChannelStatus("connected");
98
+ await flushPromises();
99
+ expect(sentOfType(harness, "user_message")).toEqual([
100
+ expect.objectContaining({ content: PROMPT }),
101
+ ]);
102
+ });
103
+
67
104
  it("are unavailable without a session, and starting then does nothing", () => {
68
105
  const actions = publicAssistantActions(shallowRef(null));
69
106
  expect(actions.isAvailable).toBe(false);
@@ -0,0 +1,142 @@
1
+ // @vitest-environment jsdom
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import {
4
+ BLOCK_SETTLE_BUDGET_MS,
5
+ awaitSettledBlocks,
6
+ readBlocks,
7
+ } from "../app/runtime/block-health";
8
+
9
+ function addBlock(
10
+ state: string,
11
+ type: string,
12
+ path?: string,
13
+ parent: Element = document.body,
14
+ ): HTMLElement {
15
+ const element = document.createElement("div");
16
+ element.setAttribute("data-dms-block-state", state);
17
+ element.setAttribute("data-dms-block-type", type);
18
+ if (path !== undefined) element.setAttribute("data-dms-block", path);
19
+ parent.append(element);
20
+ return element;
21
+ }
22
+
23
+ const never = new AbortController().signal;
24
+
25
+ describe("readBlocks", () => {
26
+ beforeEach(() => {
27
+ document.body.replaceChildren();
28
+ });
29
+
30
+ it("reads the state, kind and layout path each block states on its root", () => {
31
+ addBlock("empty", "ChartCard", "revenue");
32
+ addBlock("ok", "KpiCard");
33
+ document.body.append(document.createElement("section"));
34
+ expect(readBlocks(document)).toEqual([
35
+ { path: "revenue", type: "ChartCard", state: "empty" },
36
+ { type: "KpiCard", state: "ok" },
37
+ ]);
38
+ });
39
+
40
+ it("finds none on a host that states nothing", () => {
41
+ document.body.append(document.createElement("section"));
42
+ expect(readBlocks(document)).toEqual([]);
43
+ });
44
+ });
45
+
46
+ describe("awaitSettledBlocks", () => {
47
+ beforeEach(() => {
48
+ document.body.replaceChildren();
49
+ vi.useFakeTimers();
50
+ });
51
+
52
+ afterEach(() => {
53
+ vi.useRealTimers();
54
+ });
55
+
56
+ it("reports the blocks showing no data once the page stopped changing", async () => {
57
+ addBlock("ok", "KpiCard", "orders");
58
+ addBlock("empty", "ChartCard", "revenue");
59
+ addBlock("error", "TopListCard", "top");
60
+ const settled = awaitSettledBlocks({ root: document, signal: never });
61
+ await vi.advanceTimersByTimeAsync(BLOCK_SETTLE_BUDGET_MS);
62
+ await expect(settled).resolves.toEqual({
63
+ status: "settled",
64
+ checked: 3,
65
+ blocks: [
66
+ { path: "revenue", type: "ChartCard", state: "empty" },
67
+ { path: "top", type: "TopListCard", state: "error" },
68
+ ],
69
+ });
70
+ });
71
+
72
+ it("waits for a loading block and reports what it turned into", async () => {
73
+ const chart = addBlock("loading", "ChartCard", "revenue");
74
+ let result: Awaited<ReturnType<typeof awaitSettledBlocks>> | undefined;
75
+ void awaitSettledBlocks({ root: document, signal: never }).then((value) => {
76
+ result = value;
77
+ });
78
+ await vi.advanceTimersByTimeAsync(4_000);
79
+ expect(result).toBeUndefined();
80
+ chart.setAttribute("data-dms-block-state", "empty");
81
+ await vi.advanceTimersByTimeAsync(4_000);
82
+ expect(result).toEqual({
83
+ status: "settled",
84
+ checked: 1,
85
+ blocks: [{ path: "revenue", type: "ChartCard", state: "empty" }],
86
+ });
87
+ });
88
+
89
+ it("waits for a block that mounts after the others settled", async () => {
90
+ addBlock("ok", "KpiCard", "orders");
91
+ const settled = awaitSettledBlocks({ root: document, signal: never });
92
+ await vi.advanceTimersByTimeAsync(300);
93
+ addBlock("loading", "ChartCard", "late");
94
+ await vi.advanceTimersByTimeAsync(BLOCK_SETTLE_BUDGET_MS);
95
+ await expect(settled).resolves.toMatchObject({
96
+ status: "timeout",
97
+ checked: 2,
98
+ blocks: [{ path: "late", type: "ChartCard", state: "loading" }],
99
+ });
100
+ });
101
+
102
+ it("gives up at the budget and names the blocks still loading", async () => {
103
+ addBlock("loading", "Chart", "slow");
104
+ let finishedAt = -1;
105
+ const started = Date.now();
106
+ const settled = awaitSettledBlocks({ root: document, signal: never });
107
+ void settled.then(() => {
108
+ finishedAt = Date.now() - started;
109
+ });
110
+ await vi.advanceTimersByTimeAsync(BLOCK_SETTLE_BUDGET_MS - 1);
111
+ expect(finishedAt).toBe(-1);
112
+ await vi.advanceTimersByTimeAsync(1);
113
+ await expect(settled).resolves.toEqual({
114
+ status: "timeout",
115
+ checked: 1,
116
+ blocks: [{ path: "slow", type: "Chart", state: "loading" }],
117
+ });
118
+ expect(finishedAt).toBe(BLOCK_SETTLE_BUDGET_MS);
119
+ });
120
+
121
+ it("reports a page without data blocks as having none", async () => {
122
+ const settled = awaitSettledBlocks({ root: document, signal: never });
123
+ await vi.advanceTimersByTimeAsync(BLOCK_SETTLE_BUDGET_MS);
124
+ await expect(settled).resolves.toEqual({
125
+ status: "settled",
126
+ checked: 0,
127
+ blocks: [],
128
+ });
129
+ });
130
+
131
+ it("answers at once as superseded when another navigation starts", async () => {
132
+ addBlock("loading", "Chart", "slow");
133
+ const controller = new AbortController();
134
+ const settled = awaitSettledBlocks({
135
+ root: document,
136
+ signal: controller.signal,
137
+ });
138
+ await vi.advanceTimersByTimeAsync(100);
139
+ controller.abort();
140
+ await expect(settled).resolves.toMatchObject({ status: "superseded" });
141
+ });
142
+ });