mulmoclaude 0.5.2 → 0.6.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 (486) hide show
  1. package/Dockerfile.sandbox +100 -0
  2. package/README.md +17 -4
  3. package/bin/mulmoclaude.js +46 -15
  4. package/bin/prepare-dist.js +18 -2
  5. package/client/assets/chunk-CernVdwh.js +1 -0
  6. package/client/assets/chunk-D8eiyYIV-C1eAZMzz.js +1 -0
  7. package/client/assets/html2canvas-CDGcmOD3-BbPeutDg.js +5 -0
  8. package/client/assets/index-BbgSjFQ8.js +4968 -0
  9. package/client/assets/index-ECD0lgIv.css +2 -0
  10. package/client/assets/{index.es-D4YyL_Dg-BgT6a3Nd.js → index.es-DqtpmBm8-DJdTPdnc.js} +5 -5
  11. package/client/assets/material-symbols-outlined-BLDfUw-_.woff2 +0 -0
  12. package/client/assets/runtime-protocol-vue-6WYa8hAs.js +1 -0
  13. package/client/assets/runtime-vue-BVUzgYGA.js +1 -0
  14. package/client/assets/typeof-DBp4T-Ny-C2xoZtcz.js +1 -0
  15. package/client/assets/vue-1e_vz2LW.js +1 -0
  16. package/client/assets/vue.runtime.esm-bundler-DQ8Kjjui.js +4 -0
  17. package/client/index.html +33 -2
  18. package/package.json +20 -18
  19. package/sandbox-entrypoint.sh +106 -0
  20. package/server/accounting/accountNormalize.ts +32 -0
  21. package/server/accounting/defaultAccounts.ts +87 -0
  22. package/server/accounting/eventPublisher.ts +51 -0
  23. package/server/accounting/journal.ts +252 -0
  24. package/server/accounting/openingBalances.ts +114 -0
  25. package/server/accounting/report.ts +237 -0
  26. package/server/accounting/service.ts +718 -0
  27. package/server/accounting/snapshotCache.ts +333 -0
  28. package/server/accounting/timeSeries.ts +265 -0
  29. package/server/accounting/types.ts +148 -0
  30. package/server/agent/activeTools.ts +128 -0
  31. package/server/agent/attachmentConverter.ts +10 -5
  32. package/server/agent/backend/claude-code.ts +8 -2
  33. package/server/agent/backend/types.ts +1 -1
  34. package/server/agent/config.ts +101 -31
  35. package/server/agent/index.ts +45 -33
  36. package/server/agent/mcp-server.ts +146 -69
  37. package/server/agent/mcp-tools/index.ts +1 -5
  38. package/server/agent/mcp-tools/notify.ts +2 -22
  39. package/server/agent/mcp-tools/x.ts +0 -4
  40. package/server/agent/mcpHealth.ts +168 -0
  41. package/server/agent/plugin-names.ts +20 -77
  42. package/server/agent/prompt.ts +259 -51
  43. package/server/agent/resumeFailover.ts +1 -1
  44. package/server/agent/stream.ts +0 -1
  45. package/server/api/auth/bearerAuth.ts +5 -5
  46. package/server/api/csrfGuard.ts +1 -1
  47. package/server/api/routes/accounting.ts +366 -0
  48. package/server/api/routes/agent.ts +509 -46
  49. package/server/api/routes/attachment.ts +104 -0
  50. package/server/api/routes/chart.ts +2 -1
  51. package/server/api/routes/config.ts +12 -12
  52. package/server/api/routes/files.ts +105 -48
  53. package/server/api/routes/image.ts +70 -25
  54. package/server/api/routes/journal.ts +35 -0
  55. package/server/api/routes/mulmo-script.ts +358 -118
  56. package/server/api/routes/mulmoScriptValidate.ts +1 -1
  57. package/server/api/routes/news.ts +1 -1
  58. package/server/api/routes/notifications.ts +92 -22
  59. package/server/api/routes/notifier.ts +98 -0
  60. package/server/api/routes/pdf.ts +188 -48
  61. package/server/api/routes/plugins.ts +34 -14
  62. package/server/api/routes/presentHtml.ts +58 -3
  63. package/server/api/routes/roles.ts +1 -8
  64. package/server/api/routes/runtime-plugin.ts +224 -0
  65. package/server/api/routes/scheduler.ts +7 -5
  66. package/server/api/routes/schedulerHandlers.ts +1 -1
  67. package/server/api/routes/schedulerTasks.ts +8 -7
  68. package/server/api/routes/sessions.ts +234 -121
  69. package/server/api/routes/skills.ts +56 -51
  70. package/server/api/routes/sources.ts +52 -45
  71. package/server/api/routes/translation.ts +44 -0
  72. package/server/api/routes/wiki/frontmatter.ts +13 -65
  73. package/server/api/routes/wiki/history.ts +261 -0
  74. package/server/api/routes/wiki/pageIndex.ts +1 -1
  75. package/server/api/routes/wiki.ts +50 -26
  76. package/server/events/file-change.ts +83 -0
  77. package/server/events/notifications.ts +247 -91
  78. package/server/events/pub-sub/index.ts +1 -1
  79. package/server/events/relay-client.ts +5 -5
  80. package/server/events/scheduler-adapter.ts +2 -2
  81. package/server/events/session-store/index.ts +110 -22
  82. package/server/events/task-manager/index.ts +10 -9
  83. package/server/index.ts +509 -33
  84. package/server/notifier/engine.ts +419 -0
  85. package/server/notifier/legacy-adapters.ts +76 -0
  86. package/server/notifier/runtime-api.ts +74 -0
  87. package/server/notifier/store.ts +70 -0
  88. package/server/notifier/types.ts +121 -0
  89. package/server/plugins/dev-loader.ts +171 -0
  90. package/server/plugins/dev-watcher.ts +150 -0
  91. package/server/plugins/diagnostics.ts +188 -0
  92. package/server/plugins/preset-list.ts +52 -0
  93. package/server/plugins/preset-loader.ts +112 -0
  94. package/server/plugins/runtime-chat-api.ts +38 -0
  95. package/server/plugins/runtime-loader.ts +430 -0
  96. package/server/plugins/runtime-registry.ts +112 -0
  97. package/server/plugins/runtime-tasks-api.ts +50 -0
  98. package/server/plugins/runtime.ts +378 -0
  99. package/server/services/translation/cache.ts +72 -0
  100. package/server/services/translation/index.ts +106 -0
  101. package/server/services/translation/llm.ts +140 -0
  102. package/server/services/translation/types.ts +35 -0
  103. package/server/system/credentials.ts +13 -2
  104. package/server/system/env.ts +6 -1
  105. package/server/system/logger/formatters.ts +46 -4
  106. package/server/system/logger/index.ts +4 -4
  107. package/server/system/logger/sinks.ts +26 -5
  108. package/server/system/logger/types.ts +2 -2
  109. package/server/utils/dev-plugin-args.d.mts +11 -0
  110. package/server/utils/dev-plugin-args.mjs +43 -0
  111. package/server/utils/errors.ts +13 -4
  112. package/server/utils/files/accounting-io.ts +295 -0
  113. package/server/utils/files/atomic.ts +17 -49
  114. package/server/utils/files/attachment-store.ts +182 -0
  115. package/server/utils/files/html-io.ts +1 -7
  116. package/server/utils/files/html-store.ts +19 -0
  117. package/server/utils/files/image-store.ts +20 -22
  118. package/server/utils/files/index.ts +5 -15
  119. package/server/utils/files/journal-io.ts +7 -35
  120. package/server/utils/files/json.ts +2 -29
  121. package/server/utils/files/markdown-image-fill.ts +6 -37
  122. package/server/utils/files/markdown-store.ts +6 -21
  123. package/server/utils/files/naming.ts +3 -39
  124. package/server/utils/files/plugins-io.ts +100 -0
  125. package/server/utils/files/reference-dirs-io.ts +1 -9
  126. package/server/utils/files/roles-io.ts +2 -10
  127. package/server/utils/files/safe.ts +17 -19
  128. package/server/utils/files/scheduler-io.ts +1 -7
  129. package/server/utils/files/scheduler-overrides-io.ts +3 -12
  130. package/server/utils/files/session-io.ts +21 -30
  131. package/server/utils/files/spreadsheet-store.ts +9 -22
  132. package/server/utils/files/translation-io.ts +46 -0
  133. package/server/utils/files/user-tasks-io.ts +1 -7
  134. package/server/utils/files/workspace-io.ts +3 -79
  135. package/server/utils/gemini.ts +33 -11
  136. package/server/utils/html/htmlArtifactSplicer.ts +41 -0
  137. package/server/utils/markdown/frontmatter.ts +112 -0
  138. package/server/utils/regex.ts +56 -0
  139. package/server/utils/router.ts +41 -0
  140. package/server/utils/slug.ts +5 -3
  141. package/server/utils/time.ts +12 -0
  142. package/server/workspace/chat-index/indexer.ts +15 -2
  143. package/server/workspace/chat-index/summarizer.ts +1 -1
  144. package/server/workspace/custom-dirs.ts +1 -1
  145. package/server/workspace/helps/gemini.md +1 -1
  146. package/server/workspace/helps/guide.md +61 -0
  147. package/server/workspace/helps/index.md +4 -0
  148. package/server/workspace/helps/presenthtml.md +80 -0
  149. package/server/workspace/helps/sandbox.md +7 -0
  150. package/server/workspace/helps/storyteller.md +101 -0
  151. package/server/workspace/helps/telegram.md +1 -0
  152. package/server/workspace/helps/wiki.md +9 -7
  153. package/server/workspace/journal/archivist-cli.ts +7 -33
  154. package/server/workspace/journal/archivist-schemas.ts +5 -43
  155. package/server/workspace/journal/dailyPass.ts +34 -187
  156. package/server/workspace/journal/diff.ts +3 -28
  157. package/server/workspace/journal/index.ts +10 -81
  158. package/server/workspace/journal/indexFile.ts +3 -24
  159. package/server/workspace/journal/latestDaily.ts +51 -0
  160. package/server/workspace/journal/memoryExtractor.ts +4 -20
  161. package/server/workspace/journal/optimizationPass.ts +4 -21
  162. package/server/workspace/journal/paths.ts +4 -23
  163. package/server/workspace/journal/state.ts +6 -29
  164. package/server/workspace/memory/io.ts +213 -0
  165. package/server/workspace/memory/llm-classifier.ts +158 -0
  166. package/server/workspace/memory/migrate.ts +263 -0
  167. package/server/workspace/memory/run.ts +84 -0
  168. package/server/workspace/memory/topic-cluster.ts +218 -0
  169. package/server/workspace/memory/topic-detect.ts +67 -0
  170. package/server/workspace/memory/topic-index-hook.ts +128 -0
  171. package/server/workspace/memory/topic-io.ts +180 -0
  172. package/server/workspace/memory/topic-migrate.ts +248 -0
  173. package/server/workspace/memory/topic-run.ts +172 -0
  174. package/server/workspace/memory/topic-swap.ts +135 -0
  175. package/server/workspace/memory/topic-types.ts +142 -0
  176. package/server/workspace/memory/types.ts +83 -0
  177. package/server/workspace/news/reader.ts +4 -5
  178. package/server/workspace/paths.ts +124 -47
  179. package/server/workspace/roles.ts +2 -11
  180. package/server/workspace/skills/parser.ts +38 -55
  181. package/server/workspace/skills/user-tasks.ts +1 -2
  182. package/server/workspace/skills-preset/mc-library/SKILL.md +188 -0
  183. package/server/workspace/skills-preset.ts +196 -0
  184. package/server/workspace/sources/fetchers/githubIssues.ts +13 -11
  185. package/server/workspace/sources/fetchers/index.ts +1 -1
  186. package/server/workspace/sources/fetchers/rssParser.ts +1 -1
  187. package/server/workspace/sources/pipeline/index.ts +2 -2
  188. package/server/workspace/sources/pipeline/notify.ts +3 -3
  189. package/server/workspace/sources/pipeline/write.ts +2 -2
  190. package/server/workspace/sources/registry.ts +39 -61
  191. package/server/workspace/sources/robots.ts +1 -1
  192. package/server/workspace/tool-trace/classify.ts +2 -1
  193. package/server/workspace/tool-trace/index.ts +1 -1
  194. package/server/workspace/tool-trace/writeSearch.ts +6 -1
  195. package/server/workspace/wiki-backlinks/index.ts +19 -7
  196. package/server/workspace/wiki-backlinks/sessionBacklinks.ts +1 -0
  197. package/server/workspace/wiki-history/hook/snapshot.mjs +98 -0
  198. package/server/workspace/wiki-history/hook/snapshot.ts +135 -0
  199. package/server/workspace/wiki-history/provision.ts +181 -0
  200. package/server/workspace/wiki-pages/io.ts +217 -0
  201. package/server/workspace/wiki-pages/snapshot.ts +380 -0
  202. package/server/workspace/workspace.ts +75 -13
  203. package/src/App.vue +115 -40
  204. package/src/_runtime/protocol-vue.ts +21 -0
  205. package/src/_runtime/vue.ts +22 -0
  206. package/src/components/ChatInput.vue +14 -10
  207. package/src/components/CopyChatButton.vue +76 -0
  208. package/src/components/FileContentRenderer.vue +67 -14
  209. package/src/components/FileTree.vue +2 -2
  210. package/src/components/FilesView.vue +17 -1
  211. package/src/components/NewsView.vue +16 -2
  212. package/src/components/NotificationBell.vue +320 -93
  213. package/src/components/PageChatComposer.vue +5 -4
  214. package/src/components/PluginLauncher.vue +42 -6
  215. package/src/components/PluginScopedRoot.vue +87 -0
  216. package/src/components/RoleSelector.vue +12 -1
  217. package/src/components/RolesView.vue +562 -0
  218. package/src/components/SentAttachmentChip.vue +102 -0
  219. package/src/components/SessionHistoryPanel.vue +109 -20
  220. package/src/components/SessionRoleIcon.vue +7 -4
  221. package/src/components/SessionSidebar.vue +20 -7
  222. package/src/components/SessionTabBar.vue +1 -1
  223. package/src/components/SettingsMcpTab.vue +4 -4
  224. package/src/components/SettingsModal.vue +2 -0
  225. package/src/components/SidebarHeader.vue +16 -5
  226. package/src/components/SourcesManager.vue +23 -9
  227. package/src/components/SourcesView.vue +1 -1
  228. package/src/components/StackView.vue +102 -6
  229. package/src/components/SuggestionsPanel.vue +105 -16
  230. package/src/components/SystemFileBanner.vue +1 -1
  231. package/src/components/TodoExplorer.vue +4 -5
  232. package/src/components/todo/TodoAddDialog.vue +2 -3
  233. package/src/components/todo/TodoEditDialog.vue +1 -2
  234. package/src/components/todo/TodoEditPanel.vue +2 -3
  235. package/src/components/todo/TodoKanbanView.vue +8 -5
  236. package/src/components/todo/TodoListView.vue +3 -5
  237. package/src/components/todo/TodoTableView.vue +7 -5
  238. package/src/composables/useAccountingChannel.ts +58 -0
  239. package/src/composables/useActiveSession.ts +4 -25
  240. package/src/composables/useAppApi.ts +6 -44
  241. package/src/composables/useClipboardCopy.ts +3 -20
  242. package/src/composables/useContentDisplay.ts +33 -2
  243. package/src/composables/useDevPluginReload.ts +23 -0
  244. package/src/composables/useDynamicFavicon.ts +5 -31
  245. package/src/composables/useEventListeners.ts +0 -20
  246. package/src/composables/useExpandedDirs.ts +4 -15
  247. package/src/composables/useFaviconState.ts +12 -46
  248. package/src/composables/useFileChange.ts +53 -0
  249. package/src/composables/useFreshPluginData.ts +6 -43
  250. package/src/composables/useHealth.ts +14 -43
  251. package/src/composables/useImageErrorRepair.ts +104 -0
  252. package/src/composables/useLatestDaily.ts +40 -0
  253. package/src/composables/useMarkdownDoc.ts +39 -0
  254. package/src/composables/useMarkdownLinkHandler.ts +1 -1
  255. package/src/composables/useMcpTools.ts +3 -16
  256. package/src/composables/useNotifications.ts +138 -112
  257. package/src/composables/usePdfDownload.ts +17 -3
  258. package/src/composables/usePendingCalls.ts +8 -26
  259. package/src/composables/usePluginErrorBoundary.ts +68 -0
  260. package/src/composables/usePubSub.ts +9 -17
  261. package/src/composables/useRunElapsed.ts +5 -22
  262. package/src/composables/useSandboxStatus.ts +4 -20
  263. package/src/composables/useSessionDerived.ts +7 -15
  264. package/src/composables/useSessionHistory.ts +70 -29
  265. package/src/composables/useSessionSync.ts +25 -3
  266. package/src/composables/useSkillsList.ts +59 -0
  267. package/src/composables/useTranslatedQueries.ts +109 -0
  268. package/src/config/apiRoutes.ts +181 -80
  269. package/src/config/historyFilters.ts +5 -3
  270. package/src/config/hostEvents.ts +17 -0
  271. package/src/config/mcpCatalog.ts +277 -5
  272. package/src/config/pubsubChannels.ts +134 -12
  273. package/src/config/roles.ts +212 -147
  274. package/src/config/systemFileDescriptors.ts +5 -5
  275. package/src/config/toolNames.ts +52 -30
  276. package/src/config/workspacePaths.ts +26 -2
  277. package/src/lang/de.ts +483 -27
  278. package/src/lang/en.ts +448 -27
  279. package/src/lang/es.ts +474 -27
  280. package/src/lang/fr.ts +476 -27
  281. package/src/lang/ja.ts +465 -27
  282. package/src/lang/ko.ts +466 -27
  283. package/src/lang/pt-BR.ts +473 -27
  284. package/src/lang/zh.ts +463 -27
  285. package/src/lib/vue-i18n.ts +1 -1
  286. package/src/lib/wiki-page/slug.ts +66 -0
  287. package/src/main.ts +85 -0
  288. package/src/plugins/_extras.ts +58 -0
  289. package/src/plugins/_generated/metas.ts +42 -0
  290. package/src/plugins/_generated/registrations.ts +44 -0
  291. package/src/plugins/_generated/server-bindings.ts +47 -0
  292. package/src/plugins/accounting/Preview.vue +106 -0
  293. package/src/plugins/accounting/View.vue +632 -0
  294. package/src/plugins/accounting/actions.ts +34 -0
  295. package/src/plugins/accounting/api.ts +301 -0
  296. package/src/plugins/accounting/components/AccountEditor.vue +250 -0
  297. package/src/plugins/accounting/components/AccountRow.vue +50 -0
  298. package/src/plugins/accounting/components/AccountsList.vue +102 -0
  299. package/src/plugins/accounting/components/AccountsModal.vue +300 -0
  300. package/src/plugins/accounting/components/BalanceSheet.vue +186 -0
  301. package/src/plugins/accounting/components/BookSettings.vue +284 -0
  302. package/src/plugins/accounting/components/BookSwitcher.vue +78 -0
  303. package/src/plugins/accounting/components/DateRangePicker.vue +140 -0
  304. package/src/plugins/accounting/components/JournalEntryForm.vue +504 -0
  305. package/src/plugins/accounting/components/JournalList.vue +553 -0
  306. package/src/plugins/accounting/components/Ledger.vue +206 -0
  307. package/src/plugins/accounting/components/NewBookForm.vue +211 -0
  308. package/src/plugins/accounting/components/OpeningBalancesForm.vue +271 -0
  309. package/src/plugins/accounting/components/ProfitLoss.vue +160 -0
  310. package/src/plugins/accounting/components/accountDraft.ts +13 -0
  311. package/src/plugins/accounting/components/accountNumbering.ts +103 -0
  312. package/src/plugins/accounting/components/accountValidation.ts +75 -0
  313. package/src/plugins/accounting/components/useLatestRequest.ts +44 -0
  314. package/src/plugins/accounting/countries.ts +158 -0
  315. package/src/plugins/accounting/currencies.ts +64 -0
  316. package/src/plugins/accounting/dates.ts +51 -0
  317. package/src/plugins/accounting/definition.ts +199 -0
  318. package/src/plugins/accounting/fiscalYear.ts +136 -0
  319. package/src/plugins/accounting/index.ts +49 -0
  320. package/src/plugins/accounting/meta.ts +91 -0
  321. package/src/plugins/accounting/timeSeriesEnums.ts +16 -0
  322. package/src/plugins/api.ts +125 -0
  323. package/src/plugins/canvas/View.vue +38 -28
  324. package/src/plugins/canvas/definition.ts +10 -8
  325. package/src/plugins/canvas/index.ts +15 -8
  326. package/src/plugins/canvas/meta.ts +12 -0
  327. package/src/plugins/chart/Preview.vue +1 -1
  328. package/src/plugins/chart/View.vue +2 -2
  329. package/src/plugins/chart/definition.ts +12 -2
  330. package/src/plugins/chart/index.ts +15 -7
  331. package/src/plugins/chart/meta.ts +18 -0
  332. package/src/plugins/editImages/definition.ts +44 -0
  333. package/src/plugins/editImages/index.ts +43 -0
  334. package/src/plugins/editImages/meta.ts +5 -0
  335. package/src/plugins/generateImage/View.vue +3 -1
  336. package/src/plugins/generateImage/definition.ts +2 -0
  337. package/src/plugins/generateImage/index.ts +13 -5
  338. package/src/plugins/generateImage/meta.ts +5 -0
  339. package/src/plugins/index.ts +35 -0
  340. package/src/plugins/manageRoles/Preview.vue +7 -4
  341. package/src/plugins/manageRoles/View.vue +12 -8
  342. package/src/plugins/manageRoles/definition.ts +6 -0
  343. package/src/plugins/manageRoles/index.ts +7 -6
  344. package/src/plugins/manageSkills/View.vue +11 -7
  345. package/src/plugins/manageSkills/definition.ts +4 -1
  346. package/src/plugins/manageSkills/index.ts +14 -7
  347. package/src/plugins/manageSkills/meta.ts +21 -0
  348. package/src/plugins/manageSource/definition.ts +4 -1
  349. package/src/plugins/manageSource/index.ts +15 -7
  350. package/src/plugins/manageSource/meta.ts +21 -0
  351. package/src/plugins/markdown/Preview.vue +10 -8
  352. package/src/plugins/markdown/View.vue +84 -17
  353. package/src/plugins/markdown/definition.ts +7 -1
  354. package/src/plugins/markdown/index.ts +15 -8
  355. package/src/plugins/markdown/meta.ts +16 -0
  356. package/src/plugins/meta-types.ts +97 -0
  357. package/src/plugins/metas.ts +224 -0
  358. package/src/plugins/presentForm/Preview.vue +4 -15
  359. package/src/plugins/presentForm/View.vue +35 -78
  360. package/src/plugins/presentForm/definition.ts +7 -6
  361. package/src/plugins/presentForm/index.ts +12 -5
  362. package/src/plugins/presentForm/meta.ts +11 -0
  363. package/src/plugins/presentForm/plugin.ts +8 -9
  364. package/src/plugins/presentForm/types.ts +0 -24
  365. package/src/plugins/presentHtml/Preview.vue +1 -8
  366. package/src/plugins/presentHtml/View.vue +401 -30
  367. package/src/plugins/presentHtml/definition.ts +8 -5
  368. package/src/plugins/presentHtml/index.ts +15 -8
  369. package/src/plugins/presentHtml/meta.ts +14 -0
  370. package/src/plugins/presentMulmoScript/View.vue +327 -107
  371. package/src/plugins/presentMulmoScript/definition.ts +34 -7
  372. package/src/plugins/presentMulmoScript/helpers.ts +4 -5
  373. package/src/plugins/presentMulmoScript/index.ts +20 -7
  374. package/src/plugins/presentMulmoScript/meta.ts +52 -0
  375. package/src/plugins/scheduler/AutomationsPreview.vue +2 -8
  376. package/src/plugins/scheduler/Preview.vue +5 -2
  377. package/src/plugins/scheduler/TasksTab.vue +16 -36
  378. package/src/plugins/scheduler/View.vue +22 -54
  379. package/src/plugins/scheduler/automationsDefinition.ts +14 -9
  380. package/src/plugins/scheduler/automationsMeta.ts +5 -0
  381. package/src/plugins/scheduler/calendarDefinition.ts +4 -7
  382. package/src/plugins/scheduler/calendarMeta.ts +28 -0
  383. package/src/plugins/scheduler/formatSchedule.ts +6 -24
  384. package/src/plugins/scheduler/index.ts +26 -52
  385. package/src/plugins/scope.ts +57 -0
  386. package/src/plugins/server-bindings-types.ts +38 -0
  387. package/src/plugins/server.ts +32 -0
  388. package/src/plugins/skill/Preview.vue +25 -0
  389. package/src/plugins/skill/View.vue +125 -0
  390. package/src/plugins/skill/definition.ts +23 -0
  391. package/src/plugins/skill/index.ts +36 -0
  392. package/src/plugins/skill/plugin.ts +31 -0
  393. package/src/plugins/skill/types.ts +21 -0
  394. package/src/plugins/spreadsheet/Preview.vue +1 -3
  395. package/src/plugins/spreadsheet/View.vue +29 -49
  396. package/src/plugins/spreadsheet/cellHighlights.ts +2 -3
  397. package/src/plugins/spreadsheet/definition.ts +5 -2
  398. package/src/plugins/spreadsheet/index.ts +15 -8
  399. package/src/plugins/spreadsheet/keyboardNav.ts +38 -0
  400. package/src/plugins/spreadsheet/meta.ts +14 -0
  401. package/src/plugins/textResponse/Preview.vue +9 -1
  402. package/src/plugins/textResponse/View.vue +59 -8
  403. package/src/plugins/textResponse/index.ts +11 -3
  404. package/src/plugins/textResponse/plugin.ts +8 -10
  405. package/src/plugins/textResponse/types.ts +28 -0
  406. package/src/plugins/wiki/Preview.vue +6 -4
  407. package/src/plugins/wiki/View.vue +463 -254
  408. package/src/plugins/wiki/components/WikiPageBody.vue +159 -0
  409. package/src/plugins/wiki/helpers.ts +17 -0
  410. package/src/plugins/wiki/history/HistoryDetail.vue +325 -0
  411. package/src/plugins/wiki/history/HistoryTab.vue +167 -0
  412. package/src/plugins/wiki/history/RestoreConfirm.vue +63 -0
  413. package/src/plugins/wiki/history/api.ts +52 -0
  414. package/src/plugins/wiki/history/diff.ts +145 -0
  415. package/src/plugins/wiki/index.ts +42 -32
  416. package/src/plugins/wiki/meta.ts +10 -0
  417. package/src/plugins/wiki/pageEditLoader.ts +53 -0
  418. package/src/plugins/wiki/route.ts +8 -0
  419. package/src/router/guards.ts +2 -1
  420. package/src/router/index.ts +19 -0
  421. package/src/router/pageRoutes.ts +1 -0
  422. package/src/tools/index.ts +50 -51
  423. package/src/tools/runtimeLoader.ts +141 -0
  424. package/src/tools/types.ts +44 -1
  425. package/src/types/notification.ts +23 -0
  426. package/src/types/pastedFile.ts +10 -0
  427. package/src/types/session.ts +61 -3
  428. package/src/types/sse.ts +21 -6
  429. package/src/utils/agent/eventDispatch.ts +12 -9
  430. package/src/utils/agent/pastedAttachment.ts +35 -0
  431. package/src/utils/agent/request.ts +32 -3
  432. package/src/utils/agent/toolCalls.ts +7 -1
  433. package/src/utils/api.ts +1 -1
  434. package/src/utils/chat/exportMarkdown.ts +243 -0
  435. package/src/utils/errors.ts +10 -2
  436. package/src/utils/files/expandedDirs.ts +1 -1
  437. package/src/utils/filesPreview/todoPreview.ts +13 -2
  438. package/src/utils/format/date.ts +1 -3
  439. package/src/utils/format/jsonSyntax.ts +5 -0
  440. package/src/utils/html/iframeHeightReporterScript.ts +62 -0
  441. package/src/utils/html/previewCsp.ts +29 -2
  442. package/src/utils/image/htmlSrcAttrs.ts +122 -0
  443. package/src/utils/image/imageRepairInlineScript.ts +115 -0
  444. package/src/utils/image/resolve.ts +17 -3
  445. package/src/utils/image/rewriteMarkdownImageRefs.ts +62 -9
  446. package/src/utils/markdown/frontmatter.ts +125 -0
  447. package/src/utils/markdown/taskList.ts +7 -2
  448. package/src/utils/plugin/runtime.ts +132 -0
  449. package/src/utils/session/mergeSessions.ts +40 -37
  450. package/src/utils/session/sessionEntries.ts +74 -18
  451. package/src/utils/session/sessionHelpers.ts +54 -10
  452. package/src/utils/tools/result.ts +76 -14
  453. package/src/vite-env.d.ts +6 -0
  454. package/client/assets/html2canvas-Cx501zZr-Bug0qRNv.js +0 -5
  455. package/client/assets/index-CY-WpQUm.css +0 -2
  456. package/client/assets/index-DbTz2Mfs.js +0 -4911
  457. package/client/assets/material-symbols-outlined-NzYEeyps.woff2 +0 -0
  458. package/server/api/routes/html.ts +0 -114
  459. package/server/api/routes/todos.ts +0 -293
  460. package/server/api/routes/todosColumnsHandlers.ts +0 -333
  461. package/server/api/routes/todosHandlers.ts +0 -274
  462. package/server/api/routes/todosItemsHandlers.ts +0 -386
  463. package/server/utils/files/todos-io.ts +0 -29
  464. package/src/components/NotificationToast.vue +0 -75
  465. package/src/plugins/editImage/definition.ts +0 -27
  466. package/src/plugins/editImage/index.ts +0 -37
  467. package/src/plugins/presentHtml/helpers.ts +0 -72
  468. package/src/plugins/scheduler/LegacySchedulerView.vue +0 -32
  469. package/src/plugins/scheduler/legacyShape.ts +0 -34
  470. package/src/plugins/todo/Preview.vue +0 -68
  471. package/src/plugins/todo/View.vue +0 -378
  472. package/src/plugins/todo/composables/useTodos.ts +0 -179
  473. package/src/plugins/todo/definition.ts +0 -45
  474. package/src/plugins/todo/index.ts +0 -62
  475. package/src/plugins/todo/labels.ts +0 -163
  476. package/src/plugins/todo/priority.ts +0 -98
  477. package/src/plugins/todo/viewModes.ts +0 -19
  478. package/src/plugins/wiki/definition.ts +0 -25
  479. package/src/tools/legacyPluginNames.ts +0 -13
  480. package/src/utils/format/frontmatter.ts +0 -80
  481. package/src/utils/image/rewriteHtmlImageRefs.ts +0 -50
  482. package/src/utils/notification/dispatch.ts +0 -58
  483. /package/client/assets/{purify.es-Fx1Nqyry-BwJECkqS.js → purify.es-Fx1Nqyry-BSVNht6S.js} +0 -0
  484. /package/src/plugins/{editImage → editImages}/Preview.vue +0 -0
  485. /package/src/plugins/{editImage → editImages}/View.vue +0 -0
  486. /package/src/{config/schedulerActions.ts → plugins/scheduler/actions.ts} +0 -0
@@ -0,0 +1,63 @@
1
+ <script setup lang="ts">
2
+ import { useI18n } from "vue-i18n";
3
+ import { formatSmartTime } from "../../../utils/format/date";
4
+ import type { SnapshotSummary } from "./api";
5
+
6
+ const props = defineProps<{
7
+ snapshot: SnapshotSummary;
8
+ /** True while the parent's POST is in flight; disables both
9
+ * buttons and surfaces a spinner on the action button. */
10
+ restoring: boolean;
11
+ }>();
12
+
13
+ const emit = defineEmits<{
14
+ cancel: [];
15
+ confirm: [];
16
+ }>();
17
+
18
+ const { t } = useI18n();
19
+
20
+ function editorLabel(editor: SnapshotSummary["editor"]): string {
21
+ if (editor === "llm") return t("pluginWiki.history.editorBadgeLLM");
22
+ if (editor === "system") return t("pluginWiki.history.editorBadgeSystem");
23
+ return t("pluginWiki.history.editorBadgeUser");
24
+ }
25
+ </script>
26
+
27
+ <template>
28
+ <div
29
+ class="fixed inset-0 z-40 flex items-center justify-center bg-black/40"
30
+ data-testid="wiki-history-restore-confirm"
31
+ @click.self="!restoring && emit('cancel')"
32
+ >
33
+ <div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-4 p-5">
34
+ <h3 class="text-lg font-semibold text-gray-800 mb-3">
35
+ {{ t("pluginWiki.history.restoreConfirmTitle") }}
36
+ </h3>
37
+ <p class="text-sm text-gray-700 leading-relaxed">
38
+ {{ t("pluginWiki.history.restoreConfirmBody", { ts: formatSmartTime(props.snapshot.ts), editor: editorLabel(props.snapshot.editor) }) }}
39
+ </p>
40
+ <div class="mt-5 flex items-center justify-end gap-2">
41
+ <button
42
+ type="button"
43
+ class="h-8 px-3 rounded text-sm text-gray-700 hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
44
+ :disabled="restoring"
45
+ data-testid="wiki-history-restore-cancel"
46
+ @click="emit('cancel')"
47
+ >
48
+ {{ t("pluginWiki.history.restoreConfirmCancel") }}
49
+ </button>
50
+ <button
51
+ type="button"
52
+ class="h-8 px-3 rounded text-sm bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-60 disabled:cursor-not-allowed flex items-center gap-1 transition-colors"
53
+ :disabled="restoring"
54
+ data-testid="wiki-history-restore-confirm-action"
55
+ @click="emit('confirm')"
56
+ >
57
+ <span v-if="restoring" class="material-icons text-base animate-spin">progress_activity</span>
58
+ {{ t("pluginWiki.history.restoreConfirmAction") }}
59
+ </button>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </template>
@@ -0,0 +1,52 @@
1
+ import { apiGet, apiPost, type ApiResult } from "../../../utils/api.js";
2
+ import { pluginEndpoints } from "../../api.js";
3
+ import type { WikiEndpoints } from "../index.js";
4
+
5
+ export interface SnapshotSummary {
6
+ stamp: string;
7
+ bytes: number;
8
+ ts: string;
9
+ editor: "user" | "llm" | "system";
10
+ sessionId?: string;
11
+ reason?: string;
12
+ }
13
+
14
+ export interface SnapshotContent extends SnapshotSummary {
15
+ meta: Record<string, unknown>;
16
+ body: string;
17
+ }
18
+
19
+ interface ListResponse {
20
+ slug: string;
21
+ snapshots: SnapshotSummary[];
22
+ }
23
+
24
+ interface ReadResponse {
25
+ slug: string;
26
+ snapshot: SnapshotContent;
27
+ }
28
+
29
+ interface RestoreResponse {
30
+ slug: string;
31
+ restored: { fromStamp: string };
32
+ }
33
+
34
+ function fillRoute(template: string, params: Record<string, string>): string {
35
+ return template.replace(/:([a-zA-Z]+)/g, (_, key: string) => encodeURIComponent(params[key]));
36
+ }
37
+
38
+ function endpoints(): WikiEndpoints {
39
+ return pluginEndpoints<WikiEndpoints>("wiki");
40
+ }
41
+
42
+ export function fetchHistoryList(slug: string): Promise<ApiResult<ListResponse>> {
43
+ return apiGet<ListResponse>(fillRoute(endpoints().pageHistory, { slug }));
44
+ }
45
+
46
+ export function fetchHistorySnapshot(slug: string, stamp: string): Promise<ApiResult<ReadResponse>> {
47
+ return apiGet<ReadResponse>(fillRoute(endpoints().pageHistorySnapshot, { slug, stamp }));
48
+ }
49
+
50
+ export function restoreHistorySnapshot(slug: string, stamp: string): Promise<ApiResult<RestoreResponse>> {
51
+ return apiPost<RestoreResponse>(fillRoute(endpoints().pageHistoryRestore, { slug, stamp }));
52
+ }
@@ -0,0 +1,145 @@
1
+ import { diffLines } from "diff";
2
+
3
+ // Keys that `writeWikiPage` auto-stamps on every save. Diffing
4
+ // these would surface a change on every snapshot even when the
5
+ // user touched nothing meaningful (Q5 in the PR 3 plan).
6
+ const AUTO_STAMP_KEYS = ["updated", "editor"] as const;
7
+
8
+ export const DIFF_LINE_KIND = {
9
+ context: "context",
10
+ add: "add",
11
+ del: "del",
12
+ } as const;
13
+ export type DiffLineKind = (typeof DIFF_LINE_KIND)[keyof typeof DIFF_LINE_KIND];
14
+
15
+ export interface DiffLine {
16
+ kind: DiffLineKind;
17
+ text: string;
18
+ }
19
+
20
+ /** A contiguous viewport into the diff. Between hunks there are
21
+ * unchanged lines that have been collapsed to keep the view
22
+ * readable; the renderer surfaces those gaps via `hiddenBefore`
23
+ * and (on the last hunk only) `hiddenAfter`. */
24
+ export interface DiffHunk {
25
+ lines: DiffLine[];
26
+ /** Unchanged lines collapsed before this hunk (i.e. between the
27
+ * previous hunk's end and this hunk's start, or between BOF and
28
+ * the first hunk). 0 when nothing is hidden. */
29
+ hiddenBefore: number;
30
+ /** Unchanged lines collapsed after this hunk and before EOF.
31
+ * Always 0 except on the last hunk. */
32
+ hiddenAfter: number;
33
+ }
34
+
35
+ /** Compute a line-level unified diff with ±N context.
36
+ *
37
+ * Returns an empty array when `left === right` — there are no
38
+ * changes to surface. Otherwise builds hunks: each one is a
39
+ * contiguous run that contains at least one add/del line plus up
40
+ * to `contextLines` unchanged lines on each side. When two
41
+ * changes are within `2 * contextLines` of each other their
42
+ * context windows overlap and the hunks merge. */
43
+ export function renderUnifiedDiff(left: string, right: string, contextLines = 3): DiffHunk[] {
44
+ if (left === right) return [];
45
+
46
+ const changes = diffLines(left, right);
47
+ const lines: DiffLine[] = [];
48
+ for (const change of changes) {
49
+ const kind: DiffLineKind = change.added ? DIFF_LINE_KIND.add : change.removed ? DIFF_LINE_KIND.del : DIFF_LINE_KIND.context;
50
+ for (const text of splitLines(change.value)) {
51
+ lines.push({ kind, text });
52
+ }
53
+ }
54
+
55
+ const changedIndices: number[] = [];
56
+ lines.forEach((line, idx) => {
57
+ if (line.kind !== DIFF_LINE_KIND.context) changedIndices.push(idx);
58
+ });
59
+ if (changedIndices.length === 0) return [];
60
+
61
+ const ranges = mergeWithContext(changedIndices, contextLines, lines.length);
62
+
63
+ return ranges.map((range, idx) => {
64
+ const hunkLines = lines.slice(range.start, range.end + 1);
65
+ const prevEnd = idx === 0 ? -1 : ranges[idx - 1].end;
66
+ const hiddenBefore = range.start - prevEnd - 1;
67
+ const hiddenAfter = idx === ranges.length - 1 ? lines.length - 1 - range.end : 0;
68
+ return { lines: hunkLines, hiddenBefore, hiddenAfter };
69
+ });
70
+ }
71
+
72
+ interface Range {
73
+ start: number;
74
+ end: number;
75
+ }
76
+
77
+ function mergeWithContext(changedIndices: number[], contextLines: number, totalLines: number): Range[] {
78
+ const out: Range[] = [];
79
+ for (const idx of changedIndices) {
80
+ const start = Math.max(0, idx - contextLines);
81
+ const end = Math.min(totalLines - 1, idx + contextLines);
82
+ const last = out[out.length - 1];
83
+ // Two ranges merge when their context windows are adjacent or
84
+ // overlap — the gap-of-zero case (last.end + 1 === start) is a
85
+ // legitimate merge because there are no hidden lines between.
86
+ if (last && last.end + 1 >= start) {
87
+ last.end = Math.max(last.end, end);
88
+ } else {
89
+ out.push({ start, end });
90
+ }
91
+ }
92
+ return out;
93
+ }
94
+
95
+ function splitLines(text: string): string[] {
96
+ if (text === "") return [];
97
+ const parts = text.split("\n");
98
+ // A block that ended with `\n` produces an empty trailing element
99
+ // from `split` — drop it. A block that did NOT end with `\n` (rare
100
+ // with diffLines, but possible at EOF) keeps its content.
101
+ if (parts.length > 0 && parts[parts.length - 1] === "") parts.pop();
102
+ return parts;
103
+ }
104
+
105
+ /** Strip the keys that `writeWikiPage` auto-stamps on every save
106
+ * so the diff focuses on user-meaningful frontmatter only (Q5). */
107
+ export function stripAutoStampKeys(meta: Record<string, unknown>): Record<string, unknown> {
108
+ const out: Record<string, unknown> = {};
109
+ for (const [key, value] of Object.entries(meta)) {
110
+ if ((AUTO_STAMP_KEYS as readonly string[]).includes(key)) continue;
111
+ out[key] = value;
112
+ }
113
+ return out;
114
+ }
115
+
116
+ /** Re-serialise `frontmatter + body` for diffing. The result is a
117
+ * plain string — the diff library doesn't care about YAML; it
118
+ * just needs two strings to compare. We sort frontmatter keys so
119
+ * semantic equality (same keys, possibly reordered) doesn't show
120
+ * up as a diff. */
121
+ export function joinFrontmatterAndBody(meta: Record<string, unknown>, body: string): string {
122
+ const sortedKeys = Object.keys(meta).sort();
123
+ if (sortedKeys.length === 0) return body;
124
+ const frontmatterLines = ["---"];
125
+ for (const key of sortedKeys) {
126
+ frontmatterLines.push(`${key}: ${formatYamlValue(meta[key])}`);
127
+ }
128
+ frontmatterLines.push("---");
129
+ frontmatterLines.push("");
130
+ return `${frontmatterLines.join("\n")}\n${body}`;
131
+ }
132
+
133
+ function formatYamlValue(value: unknown): string {
134
+ if (value === null || value === undefined) return "";
135
+ if (Array.isArray(value)) return `[${value.map((item) => formatYamlValue(item)).join(", ")}]`;
136
+ if (typeof value === "object") return JSON.stringify(value);
137
+ if (typeof value === "string") {
138
+ // Quote when needed — leading / trailing whitespace, special
139
+ // YAML chars, or embedded colon-space (which YAML reads as a
140
+ // mapping). Plain alphanumerics + hyphens stay unquoted.
141
+ if (/^[A-Za-z0-9_\-./]+$/.test(value)) return value;
142
+ return JSON.stringify(value);
143
+ }
144
+ return String(value);
145
+ }
@@ -1,11 +1,18 @@
1
- import type { ToolPlugin } from "../../tools/types";
2
- import type { ToolResult } from "gui-chat-protocol";
1
+ import type { PluginEntry, PluginRegistration } from "../../tools/types";
2
+ import { wrapWithScope } from "../scope";
3
3
  import View from "./View.vue";
4
4
  import Preview from "./Preview.vue";
5
- import toolDefinition from "./definition";
6
- import { apiPost } from "../../utils/api";
7
- import { API_ROUTES } from "../../config/apiRoutes";
8
- import { makeUuid } from "../../utils/id";
5
+
6
+ const TOOL_NAME = "manageWiki";
7
+
8
+ export interface WikiEndpoints {
9
+ [key: string]: string;
10
+ base: string;
11
+ pageHistory: string;
12
+ pageHistorySnapshot: string;
13
+ pageHistoryRestore: string;
14
+ internalSnapshot: string;
15
+ }
9
16
 
10
17
  export interface WikiPageEntry {
11
18
  title: string;
@@ -21,35 +28,38 @@ export interface WikiData {
21
28
  pageEntries?: WikiPageEntry[];
22
29
  pageName?: string;
23
30
  pageExists?: boolean;
31
+ // ── `page-edit` action (Stage 3a, #963) ──────────────────────
32
+ // Server emits these when an LLM Write/Edit hits a wiki page.
33
+ // The View fetches the snapshot body via /api/wiki/pages/<slug>/
34
+ // history/<stamp> and renders it inline, falling back to
35
+ // pagePath if the snapshot has been gc'd.
36
+ slug?: string;
37
+ stamp?: string;
38
+ pagePath?: string;
24
39
  }
25
40
 
26
- const wikiPlugin: ToolPlugin<WikiData> = {
27
- toolDefinition,
28
-
29
- async execute(_context, args) {
30
- const result = await apiPost<ToolResult<WikiData>>(API_ROUTES.wiki.base, args);
31
- if (!result.ok) {
32
- // Return an error ToolResult instead of throwing so execute()
33
- // stays symmetric with every other plugin in this repo. Callers
34
- // can branch on the `error` field uniformly.
35
- const prefix = result.status === 0 ? "Wiki request failed" : `Wiki API error ${result.status}`;
36
- return {
37
- toolName: "manageWiki",
38
- uuid: makeUuid(),
39
- message: `${prefix}: ${result.error}`,
40
- };
41
- }
42
- return {
43
- ...result.data,
44
- toolName: "manageWiki",
45
- uuid: result.data.uuid ?? makeUuid(),
46
- };
41
+ // View-only registry entry (Stage 3b, #963). The plugin no longer
42
+ // exposes an MCP tool to the LLM, but the canvas dispatch
43
+ // (`getPlugin("manageWiki")`) still finds it so:
44
+ // (a) the server-emitted `page-edit` action toolResult renders
45
+ // via the same `View.vue` branches the live page action used,
46
+ // (b) historical chat sessions saved with `toolName: "manageWiki"`
47
+ // continue to replay correctly.
48
+ const wikiPlugin: PluginEntry = {
49
+ toolDefinition: {
50
+ type: "function",
51
+ name: TOOL_NAME,
52
+ prompt: "[deprecated] Replaced by inline page-edit rendering (#963). Kept registered for historical chat-history rendering only.",
53
+ description: "[deprecated] Replaced by inline page-edit rendering (#963). Kept registered for historical chat-history rendering only.",
54
+ parameters: { type: "object", properties: {}, required: [] },
47
55
  },
48
-
49
- isEnabled: () => true,
50
- generatingMessage: "Loading wiki...",
51
- viewComponent: View,
52
- previewComponent: Preview,
56
+ viewComponent: wrapWithScope("wiki", View),
57
+ previewComponent: wrapWithScope("wiki", Preview),
53
58
  };
54
59
 
55
60
  export default wikiPlugin;
61
+
62
+ export const REGISTRATION: PluginRegistration = {
63
+ toolName: TOOL_NAME,
64
+ entry: wikiPlugin,
65
+ };
@@ -0,0 +1,10 @@
1
+ import { definePluginMeta } from "../meta-types";
2
+
3
+ // View-only registry entry (Stage 3b, #963). The MCP tool was removed
4
+ // from the LLM but the plugin entry stays so server-emitted
5
+ // `page-edit` toolResults render in chat. No `apiRoutes` here — the
6
+ // /api/wiki host routes (history, snapshots) stay HOST-owned because
7
+ // they're consumed by both the wiki UI and the wiki-write hook.
8
+ export const META = definePluginMeta({
9
+ toolName: "manageWiki",
10
+ });
@@ -0,0 +1,53 @@
1
+ // Loader for the `page-edit` wiki action (Stage 3a, #963). Returns
2
+ // the full markdown content (frontmatter + body) for an LLM-edit
3
+ // inline preview. Tries the snapshot file first; falls back to the
4
+ // live page if the snapshot has been gc'd; reports `deleted` when
5
+ // neither survives.
6
+
7
+ import { apiGet } from "../../utils/api";
8
+ import { pluginEndpoints } from "../api";
9
+ import type { WikiEndpoints } from "./index";
10
+ import { serializeWithFrontmatter } from "../../utils/markdown/frontmatter";
11
+
12
+ export type PageEditLoadResult = { kind: "snapshot"; content: string; ts: string } | { kind: "current"; content: string } | { kind: "deleted" };
13
+
14
+ interface SnapshotResponse {
15
+ snapshot: {
16
+ body: string;
17
+ meta: Record<string, unknown>;
18
+ ts: string;
19
+ };
20
+ }
21
+
22
+ interface CurrentPageResponse {
23
+ data: {
24
+ content?: string;
25
+ pageExists?: boolean;
26
+ };
27
+ }
28
+
29
+ /** Fetch the snapshot at `(slug, stamp)`; on 404 fall through to
30
+ * the live page (`pagePath` lives at `data/wiki/pages/<slug>.md`
31
+ * by convention, but the slug already encodes that — pagePath is
32
+ * carried along as audit metadata). */
33
+ export async function loadPageEdit(slug: string, stamp: string): Promise<PageEditLoadResult> {
34
+ const wikiEndpoints = pluginEndpoints<WikiEndpoints>("wiki");
35
+ const snap = await apiGet<SnapshotResponse>(`${wikiEndpoints.base}/pages/${encodeURIComponent(slug)}/history/${encodeURIComponent(stamp)}`);
36
+ if (snap.ok) {
37
+ const { body, meta, ts } = snap.data.snapshot;
38
+ return { kind: "snapshot", content: serializeWithFrontmatter(meta, body), ts };
39
+ }
40
+ if (snap.status !== 404) {
41
+ // Network / 5xx: surface as deleted-equivalent for now. The
42
+ // banner stays neutral ("page deleted") rather than leaking
43
+ // transient errors into the UI; refresh recovers when the
44
+ // server is healthy again.
45
+ return { kind: "deleted" };
46
+ }
47
+
48
+ const current = await apiGet<CurrentPageResponse>(`${wikiEndpoints.base}?slug=${encodeURIComponent(slug)}`);
49
+ if (current.ok && current.data.data.pageExists && typeof current.data.data.content === "string") {
50
+ return { kind: "current", content: current.data.data.content };
51
+ }
52
+ return { kind: "deleted" };
53
+ }
@@ -101,6 +101,10 @@ export function buildWikiRouteParams(target: WikiTarget): Record<string, string>
101
101
  return { section: WIKI_ROUTE_SECTION.log, slug: "" };
102
102
  case "lint_report":
103
103
  return { section: WIKI_ROUTE_SECTION.lintReport, slug: "" };
104
+ default: {
105
+ const exhaustive: never = target;
106
+ throw new Error(`unreachable WikiTarget kind: ${JSON.stringify(exhaustive)}`);
107
+ }
104
108
  }
105
109
  }
106
110
 
@@ -117,5 +121,9 @@ export function wikiActionFor(target: WikiTarget): WikiAction {
117
121
  return WIKI_ACTION.log;
118
122
  case "lint_report":
119
123
  return WIKI_ACTION.lintReport;
124
+ default: {
125
+ const exhaustive: never = target;
126
+ throw new Error(`unreachable WikiTarget kind: ${JSON.stringify(exhaustive)}`);
127
+ }
120
128
  }
121
129
  }
@@ -27,7 +27,7 @@ function isValidSessionId(value: unknown): boolean {
27
27
  export function installGuards(router: Router): void {
28
28
  router.beforeEach((dest) => {
29
29
  if (dest.name === "chat") {
30
- const sessionId = dest.params.sessionId;
30
+ const { sessionId } = dest.params;
31
31
  if (isNonEmptyString(sessionId) && !isValidSessionId(sessionId)) {
32
32
  return { name: "chat", params: {}, query: {}, replace: true };
33
33
  }
@@ -76,5 +76,6 @@ export function installGuards(router: Router): void {
76
76
  };
77
77
  }
78
78
  }
79
+ return undefined;
79
80
  });
80
81
  }
@@ -15,6 +15,7 @@
15
15
  import { defineComponent, h } from "vue";
16
16
  import { createRouter, createWebHistory, type RouteRecordRaw } from "vue-router";
17
17
  import { PAGE_ROUTES, type PageRouteName } from "./pageRoutes";
18
+ import { HOST_EVENTS } from "../config/hostEvents";
18
19
 
19
20
  // Re-export the constants so existing `import { PAGE_ROUTES } from
20
21
  // "./router"` call sites keep working. The actual definitions live
@@ -66,6 +67,10 @@ const routes: RouteRecordRaw[] = [
66
67
  // News viewer (#761). Optional `?source=<slug>` query for the
67
68
  // Sources-page deep link.
68
69
  { path: "/news", name: PAGE_ROUTES.news, component: Stub },
70
+ // Debug page (#feat-encore PR 1 follow-up). Standalone playground for
71
+ // experimental plugin features (notifier engine, etc.). Rendered by
72
+ // the @mulmoclaude/debug-plugin runtime plugin.
73
+ { path: "/debug", name: PAGE_ROUTES.debug, component: Stub },
69
74
  { path: "/:pathMatch(.*)*", redirect: "/chat" },
70
75
  ];
71
76
 
@@ -74,4 +79,18 @@ const router = createRouter({
74
79
  routes,
75
80
  });
76
81
 
82
+ // Bridge SPA navigation to a DOM CustomEvent so runtime-loaded plugins
83
+ // (which can't import vue-router without extra plumbing) can react to
84
+ // route changes without polling. Fires on every commit, including
85
+ // query-only changes that don't remount the matched component.
86
+ //
87
+ // Subscriber: `@mulmoclaude/debug-plugin` View, which uses it to
88
+ // re-evaluate `?mode=` and `?notificationId=` params after the host
89
+ // notifier popup pushes a new URL. The event name lives in
90
+ // `src/config/hostEvents.ts` so the contract isn't a magic string
91
+ // duplicated between host and plugin.
92
+ router.afterEach((toRoute) => {
93
+ window.dispatchEvent(new CustomEvent(HOST_EVENTS.routeChange, { detail: { fullPath: toRoute.fullPath } }));
94
+ });
95
+
77
96
  export default router;
@@ -18,6 +18,7 @@ export const PAGE_ROUTES = {
18
18
  roles: "roles",
19
19
  sources: "sources",
20
20
  news: "news",
21
+ debug: "debug",
21
22
  } as const;
22
23
 
23
24
  export type PageRouteName = (typeof PAGE_ROUTES)[keyof typeof PAGE_ROUTES];
@@ -1,59 +1,58 @@
1
+ // Central tool registry. Two stages of lookup:
2
+ //
3
+ // 1. Build-time-bundled plugins (the "built-in" set) — assembled
4
+ // generically from `BUILT_IN_PLUGINS` exported by
5
+ // `src/plugins/index.ts`. Each plugin self-registers via a
6
+ // `REGISTRATION` export co-locating `TOOL_NAMES.x` with its
7
+ // `PluginEntry`. Adding a new built-in plugin does NOT touch
8
+ // this file.
9
+ //
10
+ // 2. Runtime-installed plugins (#1043 C-2) — loaded from the
11
+ // workspace ledger at boot via `runtimeLoader`. Looked up
12
+ // after the built-in set so a static plugin always wins on
13
+ // collision (mirrors the server-side collision policy in
14
+ // `server/plugins/runtime-registry.ts`).
15
+
1
16
  import type { PluginEntry } from "./types";
2
- import { LEGACY_VIEW_ONLY_PLUGIN_NAMES } from "./legacyPluginNames";
3
- import textResponsePlugin from "../plugins/textResponse/index";
4
- import markdownPlugin from "../plugins/markdown/index";
5
- import spreadsheetPlugin from "../plugins/spreadsheet/index";
6
- import MindMapPlugin from "@gui-chat-plugin/mindmap/vue";
7
- import generateImagePlugin from "../plugins/generateImage/index";
8
- import QuizPlugin from "@mulmochat-plugin/quiz/vue";
9
- import presentFormPlugin from "../plugins/presentForm/index";
10
- import canvasPlugin from "../plugins/canvas/index";
11
- import editImagePlugin from "../plugins/editImage/index";
12
- import Present3DPlugin from "@gui-chat-plugin/present3d/vue";
13
- import WeatherPlugin from "@gui-chat-plugin/weather/vue";
14
- import todoPlugin from "../plugins/todo/index";
15
- import { manageCalendarPlugin, manageAutomationsPlugin, legacyManageSchedulerEntry } from "../plugins/scheduler/index";
16
- import manageRolesPlugin from "../plugins/manageRoles/index";
17
- import manageSkillsPlugin from "../plugins/manageSkills/index";
18
- import manageSourcePlugin from "../plugins/manageSource/index";
19
- import wikiPlugin from "../plugins/wiki/index";
20
- import presentMulmoScriptPlugin from "../plugins/presentMulmoScript/index";
21
- import presentHtmlPlugin from "../plugins/presentHtml/index";
22
- import presentChartPlugin from "../plugins/chart/index";
17
+ import { getRuntimePluginEntry, getRuntimeToolNames } from "./runtimeLoader";
18
+ import { BUILT_IN_PLUGINS } from "../plugins";
23
19
 
24
- const plugins: Record<string, PluginEntry> = {
25
- "text-response": textResponsePlugin.plugin,
26
- manageTodoList: todoPlugin,
27
- manageCalendar: manageCalendarPlugin,
28
- manageAutomations: manageAutomationsPlugin,
29
- // View-only fallback so chat sessions saved before #824
30
- // continue to render rich tool-result cards. Not exposed to
31
- // the LLM (absent from server/agent/plugin-names.ts and
32
- // src/config/toolNames.ts) — strictly historical rendering.
33
- manageScheduler: legacyManageSchedulerEntry,
34
- manageRoles: manageRolesPlugin,
35
- manageSkills: manageSkillsPlugin,
36
- manageSource: manageSourcePlugin,
37
- manageWiki: wikiPlugin,
38
- presentMulmoScript: presentMulmoScriptPlugin,
39
- presentDocument: markdownPlugin,
40
- presentSpreadsheet: spreadsheetPlugin,
41
- createMindMap: MindMapPlugin.plugin,
42
- generateImage: generateImagePlugin,
43
- putQuestions: QuizPlugin.plugin,
44
- presentForm: presentFormPlugin,
45
- openCanvas: canvasPlugin,
46
- presentHtml: presentHtmlPlugin,
47
- presentChart: presentChartPlugin,
48
- editImage: editImagePlugin,
49
- present3D: Present3DPlugin.plugin,
50
- weather: WeatherPlugin.plugin,
51
- };
20
+ // Build the lookup with explicit duplicate detection. `BUILT_IN_PLUGINS`
21
+ // is the union of generated registrations (one per built-in META) and
22
+ // `EXTERNAL_PLUGIN_REGISTRATIONS` (still-unmigrated plugins). The
23
+ // META aggregator already screens its half for collisions, but a
24
+ // duplicate `toolName` between an external registration and a META
25
+ // would slip past `Object.fromEntries` silently with last-writer-wins.
26
+ // Throwing at boot is the right move silent dispatch hijack is
27
+ // strictly worse than a hard error during start-up (CR review #1125).
28
+ //
29
+ // Use a null-prototype dictionary so:
30
+ // 1. The `in` / `hasOwn` check doesn't walk a prototype chain — a
31
+ // legitimate `toolName` like `"toString"` or `"hasOwnProperty"`
32
+ // can't false-positive as a duplicate.
33
+ // 2. The bracket assign at the end can't trigger `__proto__`
34
+ // prototype-mutation semantics if a future registration value
35
+ // ever flows from less-trusted code.
36
+ // Codex review on PR #1156.
37
+ const plugins: Record<string, PluginEntry> = (() => {
38
+ const out: Record<string, PluginEntry> = Object.create(null);
39
+ for (const registration of BUILT_IN_PLUGINS) {
40
+ if (Object.prototype.hasOwnProperty.call(out, registration.toolName)) {
41
+ throw new Error(`Duplicate built-in plugin registration for "${registration.toolName}"`);
42
+ }
43
+ out[registration.toolName] = registration.entry;
44
+ }
45
+ return out;
46
+ })();
52
47
 
53
48
  export function getPlugin(name: string): PluginEntry | null {
54
- return plugins[name] ?? null;
49
+ // Static (build-time) plugins win on collision — runtime plugins
50
+ // are registered in mcp-server.ts only when their tool name does
51
+ // not already exist in the static set, so this lookup order keeps
52
+ // the contracts symmetric across server and frontend.
53
+ return plugins[name] ?? getRuntimePluginEntry(name);
55
54
  }
56
55
 
57
56
  export function getAllPluginNames(): string[] {
58
- return Object.keys(plugins).filter((name) => !LEGACY_VIEW_ONLY_PLUGIN_NAMES.has(name));
57
+ return [...BUILT_IN_PLUGINS.map((registration) => registration.toolName), ...getRuntimeToolNames()];
59
58
  }