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
@@ -1,17 +1,14 @@
1
1
  <template>
2
- <div class="w-full h-full overflow-y-auto p-8">
2
+ <div class="w-full h-full overflow-y-auto p-8" data-testid="present-form-view">
3
3
  <div v-if="formData" class="max-w-3xl w-full mx-auto">
4
- <!-- Form Title -->
5
4
  <h2 v-if="formData.title" class="text-gray-900 text-3xl font-bold mb-4 text-center">
6
5
  {{ formData.title }}
7
6
  </h2>
8
7
 
9
- <!-- Form Description -->
10
8
  <p v-if="formData.description" class="text-gray-600 text-center mb-8 text-lg">
11
9
  {{ formData.description }}
12
10
  </p>
13
11
 
14
- <!-- Error Summary -->
15
12
  <div v-if="showErrorSummary && fieldErrors.size > 0" class="bg-red-50 border-2 border-red-500 rounded-lg p-4 mb-6" role="alert">
16
13
  <h3 class="text-red-800 font-semibold mb-2 flex items-center gap-2">
17
14
  <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
@@ -25,23 +22,21 @@
25
22
  </h3>
26
23
  <ul class="text-red-700 space-y-1">
27
24
  <li v-for="[fieldId, error] in fieldErrors" :key="fieldId">
28
- <a :href="`#${fieldId}`" @click.prevent="focusField(fieldId)" class="hover:underline cursor-pointer">
25
+ <a :href="`#${fieldId}`" class="hover:underline cursor-pointer" @click.prevent="focusField(fieldId)">
29
26
  {{ error.message }}
30
27
  </a>
31
28
  </li>
32
29
  </ul>
33
30
  </div>
34
31
 
35
- <!-- Form Fields -->
36
- <form @submit.prevent="handleSubmit" class="space-y-6">
32
+ <form class="space-y-6" @submit.prevent="handleSubmit">
37
33
  <div
38
34
  v-for="field in formData.fields"
39
- :key="field.id"
40
35
  :id="field.id"
36
+ :key="field.id"
41
37
  class="form-field"
42
38
  :class="{ 'has-error': hasError(field.id) && touched.has(field.id) }"
43
39
  >
44
- <!-- Field Label -->
45
40
  <label
46
41
  :for="`input-${field.id}`"
47
42
  class="block text-gray-800 font-semibold mb-2"
@@ -53,12 +48,10 @@
53
48
  <span v-if="field.required" class="text-red-500 ml-1" aria-label="required">{{ t("pluginPresentForm.requiredMarker") }}</span>
54
49
  </label>
55
50
 
56
- <!-- Field Description -->
57
51
  <p v-if="field.description" class="text-gray-600 text-sm mb-2">
58
52
  {{ field.description }}
59
53
  </p>
60
54
 
61
- <!-- Text Field -->
62
55
  <input
63
56
  v-if="field.type === 'text'"
64
57
  :id="`input-${field.id}`"
@@ -67,16 +60,15 @@
67
60
  :placeholder="field.placeholder"
68
61
  :aria-invalid="hasError(field.id) && touched.has(field.id)"
69
62
  :aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
70
- @blur="handleBlur(field.id)"
71
- @input="handleInput(field.id)"
72
63
  class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"
73
64
  :class="{
74
65
  'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
75
66
  'border-gray-300': !hasError(field.id) || !touched.has(field.id),
76
67
  }"
68
+ @blur="handleBlur(field.id)"
69
+ @input="handleInput(field.id)"
77
70
  />
78
71
 
79
- <!-- Textarea Field -->
80
72
  <textarea
81
73
  v-else-if="field.type === 'textarea'"
82
74
  :id="`input-${field.id}`"
@@ -85,16 +77,15 @@
85
77
  :rows="field.rows || 4"
86
78
  :aria-invalid="hasError(field.id) && touched.has(field.id)"
87
79
  :aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
88
- @blur="handleBlur(field.id)"
89
- @input="handleInput(field.id)"
90
80
  class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors resize-y"
91
81
  :class="{
92
82
  'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
93
83
  'border-gray-300': !hasError(field.id) || !touched.has(field.id),
94
84
  }"
85
+ @blur="handleBlur(field.id)"
86
+ @input="handleInput(field.id)"
95
87
  />
96
88
 
97
- <!-- Number Field -->
98
89
  <input
99
90
  v-else-if="field.type === 'number'"
100
91
  :id="`input-${field.id}`"
@@ -105,16 +96,15 @@
105
96
  :step="field.step"
106
97
  :aria-invalid="hasError(field.id) && touched.has(field.id)"
107
98
  :aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
108
- @blur="handleBlur(field.id)"
109
- @input="handleInput(field.id)"
110
99
  class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"
111
100
  :class="{
112
101
  'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
113
102
  'border-gray-300': !hasError(field.id) || !touched.has(field.id),
114
103
  }"
104
+ @blur="handleBlur(field.id)"
105
+ @input="handleInput(field.id)"
115
106
  />
116
107
 
117
- <!-- Date Field -->
118
108
  <input
119
109
  v-else-if="field.type === 'date'"
120
110
  :id="`input-${field.id}`"
@@ -124,16 +114,15 @@
124
114
  :max="field.maxDate"
125
115
  :aria-invalid="hasError(field.id) && touched.has(field.id)"
126
116
  :aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
127
- @blur="handleBlur(field.id)"
128
- @change="handleInput(field.id)"
129
117
  class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"
130
118
  :class="{
131
119
  'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
132
120
  'border-gray-300': !hasError(field.id) || !touched.has(field.id),
133
121
  }"
122
+ @blur="handleBlur(field.id)"
123
+ @change="handleInput(field.id)"
134
124
  />
135
125
 
136
- <!-- Time Field -->
137
126
  <input
138
127
  v-else-if="field.type === 'time'"
139
128
  :id="`input-${field.id}`"
@@ -141,16 +130,15 @@
141
130
  type="time"
142
131
  :aria-invalid="hasError(field.id) && touched.has(field.id)"
143
132
  :aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
144
- @blur="handleBlur(field.id)"
145
- @change="handleInput(field.id)"
146
133
  class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"
147
134
  :class="{
148
135
  'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
149
136
  'border-gray-300': !hasError(field.id) || !touched.has(field.id),
150
137
  }"
138
+ @blur="handleBlur(field.id)"
139
+ @change="handleInput(field.id)"
151
140
  />
152
141
 
153
- <!-- Radio Field -->
154
142
  <div
155
143
  v-else-if="field.type === 'radio'"
156
144
  class="space-y-2"
@@ -168,32 +156,31 @@
168
156
  }"
169
157
  >
170
158
  <input
159
+ v-model="formValues[field.id]"
171
160
  type="radio"
172
161
  :name="field.id"
173
162
  :value="index"
174
- v-model="formValues[field.id]"
163
+ class="mr-3 h-4 w-4 flex-shrink-0"
175
164
  @change="handleInput(field.id)"
176
165
  @blur="handleBlur(field.id)"
177
- class="mr-3 h-4 w-4 flex-shrink-0"
178
166
  />
179
167
  <span class="text-gray-800">{{ choice }}</span>
180
168
  </label>
181
169
  </div>
182
170
 
183
- <!-- Dropdown Field -->
184
171
  <select
185
172
  v-else-if="field.type === 'dropdown'"
186
173
  :id="`input-${field.id}`"
187
174
  v-model="formValues[field.id]"
188
175
  :aria-invalid="hasError(field.id) && touched.has(field.id)"
189
176
  :aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
190
- @blur="handleBlur(field.id)"
191
- @change="handleInput(field.id)"
192
177
  class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors bg-white"
193
178
  :class="{
194
179
  'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
195
180
  'border-gray-300': !hasError(field.id) || !touched.has(field.id),
196
181
  }"
182
+ @blur="handleBlur(field.id)"
183
+ @change="handleInput(field.id)"
197
184
  >
198
185
  <option :value="null" disabled>{{ t("pluginPresentForm.selectOption") }}</option>
199
186
  <option v-for="(choice, index) in field.choices" :key="index" :value="index">
@@ -201,7 +188,6 @@
201
188
  </option>
202
189
  </select>
203
190
 
204
- <!-- Checkbox Field -->
205
191
  <div
206
192
  v-else-if="field.type === 'checkbox'"
207
193
  class="space-y-2"
@@ -219,18 +205,17 @@
219
205
  }"
220
206
  >
221
207
  <input
208
+ v-model="formValues[field.id]"
222
209
  type="checkbox"
223
210
  :value="index"
224
- v-model="formValues[field.id]"
211
+ class="mr-3 h-4 w-4 flex-shrink-0"
225
212
  @change="handleInput(field.id)"
226
213
  @blur="handleBlur(field.id)"
227
- class="mr-3 h-4 w-4 flex-shrink-0"
228
214
  />
229
215
  <span class="text-gray-800">{{ choice }}</span>
230
216
  </label>
231
217
  </div>
232
218
 
233
- <!-- Error Message -->
234
219
  <div
235
220
  v-if="hasError(field.id) && touched.has(field.id)"
236
221
  :id="`${field.id}-error`"
@@ -247,7 +232,6 @@
247
232
  {{ fieldErrors.get(field.id)?.message }}
248
233
  </div>
249
234
 
250
- <!-- Character Count (for text/textarea with maxLength) -->
251
235
  <div
252
236
  v-if="showCharCount(field)"
253
237
  class="text-sm mt-2"
@@ -265,7 +249,6 @@
265
249
  </div>
266
250
  </div>
267
251
 
268
- <!-- Submit Button -->
269
252
  <div class="mt-8 flex justify-center">
270
253
  <button
271
254
  type="submit"
@@ -277,7 +260,6 @@
277
260
  </button>
278
261
  </div>
279
262
 
280
- <!-- Progress Indicator -->
281
263
  <div class="mt-4 text-center text-gray-600 text-sm">
282
264
  {{ t("pluginPresentForm.progress", { filled: filledRequiredCount, total: requiredFieldsCount }) }}
283
265
  </div>
@@ -349,7 +331,6 @@ function applyNewResult(newResult: ToolResult): void {
349
331
  isRestoring.value = false;
350
332
  }
351
333
 
352
- // Initialize form data and restore state
353
334
  watch(
354
335
  () => props.selectedResult,
355
336
  (newResult, oldResult) => {
@@ -360,7 +341,6 @@ watch(
360
341
  { immediate: true },
361
342
  );
362
343
 
363
- // Save state to viewState - watch all state changes together
364
344
  watch(
365
345
  [formValues, touched, submitted],
366
346
  () => {
@@ -416,9 +396,7 @@ function isEmpty(value: any): boolean {
416
396
  return false;
417
397
  }
418
398
 
419
- // 254 = RFC 5321 maximum address length. indexOf-based check avoids
420
- // the chained `[^\s@]+` regex pattern that sonarjs flags as
421
- // vulnerable to super-linear backtracking.
399
+ // 254 per RFC 5321; bounding upfront keeps the regex below from backtracking.
422
400
  const EMAIL_MAX_LENGTH = 254;
423
401
  function isValidEmail(email: string): boolean {
424
402
  if (!email || email.length > EMAIL_MAX_LENGTH) return false;
@@ -433,6 +411,7 @@ function isValidEmail(email: string): boolean {
433
411
 
434
412
  function isValidUrl(url: string): boolean {
435
413
  try {
414
+ // eslint-disable-next-line no-new -- side-effect probe to validate URL syntax
436
415
  new URL(url);
437
416
  return true;
438
417
  } catch {
@@ -518,10 +497,9 @@ function validateField(fieldId: string): boolean {
518
497
  type: "custom",
519
498
  });
520
499
  return false;
521
- } else {
522
- fieldErrors.value.delete(fieldId);
523
- return true;
524
500
  }
501
+ fieldErrors.value.delete(fieldId);
502
+ return true;
525
503
  }
526
504
 
527
505
  function handleBlur(fieldId: string): void {
@@ -530,7 +508,6 @@ function handleBlur(fieldId: string): void {
530
508
  }
531
509
 
532
510
  function handleInput(fieldId: string): void {
533
- // Real-time validation for fields that are already touched
534
511
  if (touched.value.has(fieldId)) {
535
512
  validateField(fieldId);
536
513
  }
@@ -554,15 +531,13 @@ function showCharCount(field: FormField): boolean {
554
531
 
555
532
  function isNearLimit(field: FormField): boolean {
556
533
  if (field.type !== "text" && field.type !== "textarea") return false;
557
- const maxLength = (field as TextField | TextareaField).maxLength;
534
+ const { maxLength } = field as TextField | TextareaField;
558
535
  if (!maxLength) return false;
559
536
  const currentLength = (formValues.value[field.id] || "").length;
560
537
  return currentLength / maxLength > 0.9;
561
538
  }
562
539
 
563
- const requiredFieldsCount = computed(() => {
564
- return formData.value?.fields.filter((field) => field.required).length || 0;
565
- });
540
+ const requiredFieldsCount = computed(() => formData.value?.fields.filter((field) => field.required).length || 0);
566
541
 
567
542
  const filledRequiredCount = computed(() => {
568
543
  if (!formData.value) return 0;
@@ -572,27 +547,18 @@ const filledRequiredCount = computed(() => {
572
547
  function handleSubmit(): void {
573
548
  if (submitted.value) return;
574
549
 
575
- // Mark all fields as touched
576
550
  formData.value?.fields.forEach((field) => {
577
551
  touched.value.add(field.id);
578
552
  validateField(field.id);
579
553
  });
580
554
 
581
- // Check for errors
582
555
  if (fieldErrors.value.size > 0) {
583
556
  showErrorSummary.value = true;
584
- // Focus first error field
585
- const firstErrorFieldId = Array.from(fieldErrors.value.keys())[0];
557
+ const [firstErrorFieldId] = Array.from(fieldErrors.value.keys());
586
558
  focusField(firstErrorFieldId);
587
559
  return;
588
560
  }
589
561
 
590
- // Build a markdown bullet list of `- {label}: {value}` lines so the
591
- // chat history reads naturally for a human while still being trivial
592
- // for the LLM to parse. Empty values become "(none)". Titles, labels,
593
- // and choice strings are passed through singleLine() to strip any
594
- // newlines they might contain — without that, a label like
595
- // "Address (street\nand number)" would shatter the bullet structure.
596
562
  const lines: string[] = [];
597
563
  if (formData.value?.title) lines.push(`**${singleLine(formData.value.title)}**`, "");
598
564
  formData.value?.fields.forEach((field) => {
@@ -603,22 +569,15 @@ function handleSubmit(): void {
603
569
  props.sendTextMessage(lines.join("\n"));
604
570
  }
605
571
 
606
- // Indent every line except the first by 2 spaces — under markdown
607
- // rules, indented continuations stay attached to the preceding bullet
608
- // instead of starting a new top-level item. Without this, a textarea
609
- // value containing newlines would inject phantom bullets into the
610
- // chat-side payload that the LLM could mis-parse.
572
+ // Indent so multi-line text/textarea values stay attached to their bullet
573
+ // instead of opening a new top-level item under markdown rules.
611
574
  function indentContinuation(text: string): string {
612
575
  return text.replace(/\n/g, "\n ");
613
576
  }
614
577
 
615
- // Collapse any newline (and surrounding whitespace) into a single
616
- // space. Used on titles, labels, and choice strings (LLM-authored
617
- // fields) so they can't smuggle a newline into the markdown bullet
618
- // structure and reshape the payload. Free-form user input
619
- // (text/textarea values) uses indentContinuation instead — it
620
- // preserves multi-line values. Implemented via split-trim-join (not a
621
- // global regex) to avoid sonarjs's slow-regex flag on `\s*\n\s*`.
578
+ // LLM-authored title/label/choice strings collapse any newline so they
579
+ // can't smuggle phantom bullets into the payload. split-trim-join avoids
580
+ // sonarjs's slow-regex flag on `\s*\n\s*`.
622
581
  function singleLine(text: string): string {
623
582
  return text
624
583
  .split(/\r?\n/)
@@ -633,14 +592,12 @@ function renderValue(field: FormField, value: any): string {
633
592
  if (field.type === "radio" || field.type === "dropdown") {
634
593
  return value !== null && value !== undefined ? singleLine(field.choices[value]) : empty;
635
594
  }
636
- // Checkbox values render as a nested bullet sublist rather than a
637
- // comma-joined string. Comma-joining is ambiguous when a choice label
638
- // itself contains a comma; the sublist is unambiguous and survives
639
- // round-trip parsing.
595
+ // Sublist (not comma-join): a choice label containing a comma would otherwise
596
+ // be indistinguishable from two separate selections.
640
597
  if (field.type === "checkbox") {
641
598
  const items: string[] = (value || []).map((idx: number) => singleLine(field.choices[idx]));
642
599
  if (items.length === 0) return empty;
643
- return "\n - " + items.join("\n - ");
600
+ return `\n - ${items.join("\n - ")}`;
644
601
  }
645
602
  if (typeof value === "string") {
646
603
  const trimmed = value.trim();
@@ -1,14 +1,13 @@
1
- /**
2
- * Form Tool Definition (Schema)
3
- */
4
-
5
1
  import type { ToolDefinition } from "gui-chat-protocol";
2
+ import { META } from "./meta";
3
+ import type { ResolvedRoute } from "../meta-types";
6
4
 
7
- export const TOOL_NAME = "presentForm";
5
+ export const TOOL_NAME = META.toolName;
6
+ export type PresentFormEndpoints = { readonly [K in keyof typeof META.apiRoutes]: ResolvedRoute };
8
7
 
9
8
  export const TOOL_DEFINITION: ToolDefinition = {
10
9
  type: "function",
11
- name: TOOL_NAME,
10
+ name: META.toolName,
12
11
  description:
13
12
  "Create a structured form to collect information from the user. Supports various field types including text input, textarea, multiple choice (radio), dropdown menus, checkboxes, date/time pickers, and number inputs. Each field can have validation rules and help text.",
14
13
  parameters: {
@@ -125,3 +124,5 @@ export const TOOL_DEFINITION: ToolDefinition = {
125
124
  required: ["fields"],
126
125
  },
127
126
  };
127
+
128
+ export default TOOL_DEFINITION;
@@ -1,18 +1,25 @@
1
1
  import type { ToolPlugin } from "gui-chat-protocol/vue";
2
+ import type { PluginRegistration } from "../../tools/types";
2
3
  import type { FormData, FormArgs } from "./types";
3
- import { TOOL_DEFINITION } from "./definition";
4
+ import { TOOL_DEFINITION, TOOL_NAME } from "./definition";
4
5
  import { executeForm } from "./plugin";
6
+ import { wrapWithScope } from "../scope";
5
7
  import View from "./View.vue";
6
8
  import Preview from "./Preview.vue";
7
9
 
8
- const presentFormPlugin: ToolPlugin<never, FormData, FormArgs> = {
10
+ const presentFormPlugin: ToolPlugin<FormData, FormData, FormArgs> = {
9
11
  toolDefinition: TOOL_DEFINITION,
10
12
  execute: executeForm,
11
13
  generatingMessage: "Preparing form...",
12
14
  isEnabled: () => true,
13
- viewComponent: View,
14
- previewComponent: Preview,
15
+ viewComponent: wrapWithScope("form", View),
16
+ previewComponent: wrapWithScope("form", Preview),
15
17
  };
16
18
 
17
19
  export default presentFormPlugin;
18
- export { TOOL_NAME } from "./definition";
20
+ export { TOOL_NAME };
21
+
22
+ export const REGISTRATION: PluginRegistration = {
23
+ toolName: TOOL_NAME,
24
+ entry: presentFormPlugin,
25
+ };
@@ -0,0 +1,11 @@
1
+ import { definePluginMeta } from "../meta-types";
2
+
3
+ export const META = definePluginMeta({
4
+ toolName: "presentForm",
5
+ apiNamespace: "form",
6
+ apiRoutes: {
7
+ /** POST /api/form — render a form for the user to fill out. */
8
+ dispatch: { method: "POST", path: "" },
9
+ },
10
+ mcpDispatch: "dispatch",
11
+ });
@@ -1,8 +1,5 @@
1
- // Local fork of @mulmochat-plugin/form (originally
2
- // ~/ss/llm/GUIChatPlugins/MulmoChatPluginForm). Forked so the form
3
- // submission payload (see View.vue:handleSubmit) renders as readable
4
- // markdown bullets in the chat history instead of a JSON-wrapped
5
- // object.
1
+ // Forked from @mulmochat-plugin/form so handleSubmit can emit a markdown
2
+ // bullet list instead of the upstream JSON-wrapped payload.
6
3
 
7
4
  import type { ToolContext, ToolResult } from "gui-chat-protocol";
8
5
  import type { FormData, FormArgs, FormField } from "./types";
@@ -31,9 +28,7 @@ function validateCheckboxRange(field: FormField & { type: "checkbox" }): void {
31
28
  if (maxSelections !== undefined && maxSelections > choices.length) {
32
29
  throw new Error(`Field '${id}': maxSelections cannot exceed number of choices`);
33
30
  }
34
- // Without this lower-bound check, a form with minSelections > choices.length
35
- // would render but be impossible to submit — the user can never satisfy the
36
- // minimum because there aren't enough options to pick.
31
+ // Without this, a form would render but be unsubmittable.
37
32
  if (minSelections !== undefined && minSelections > choices.length) {
38
33
  throw new Error(`Field '${id}': minSelections cannot exceed number of choices`);
39
34
  }
@@ -67,7 +62,7 @@ function validateField(field: FormField, index: number, seenIds: Set<string>): v
67
62
  validateRangeField(field);
68
63
  }
69
64
 
70
- export const executeForm = async (_context: ToolContext, args: FormArgs): Promise<ToolResult<never, FormData>> => {
65
+ export const executeForm = async (_context: ToolContext, args: FormArgs): Promise<ToolResult<FormData, FormData>> => {
71
66
  try {
72
67
  const { title, description, fields } = args;
73
68
  if (!fields || !Array.isArray(fields) || fields.length === 0) {
@@ -81,6 +76,10 @@ export const executeForm = async (_context: ToolContext, args: FormArgs): Promis
81
76
  const titleSuffix = title ? `: ${title}` : "";
82
77
  return {
83
78
  message: `Form created with ${fieldCount}${titleSuffix}`,
79
+ // `data` is the view's source (also the host's preview-gate
80
+ // signal); `jsonData` is what the LLM sees in the tool result.
81
+ // Same payload, two audiences — keep both in sync.
82
+ data: formData,
84
83
  jsonData: formData,
85
84
  instructions:
86
85
  "The form has been presented to the user. Wait for the user to fill out and submit it. They will reply with a markdown bullet list of `- {label}: {value}` lines.",
@@ -1,18 +1,5 @@
1
- /**
2
- * Form Plugin Types
3
- *
4
- * Form-specific type definitions only.
5
- * Common types should be imported directly from gui-chat-protocol.
6
- */
7
-
8
- // ============================================================================
9
- // Form-specific Types
10
- // ============================================================================
11
-
12
- /** Field type discriminator */
13
1
  export type FieldType = "text" | "textarea" | "radio" | "dropdown" | "checkbox" | "date" | "time" | "number";
14
2
 
15
- /** Base field interface */
16
3
  export interface BaseField {
17
4
  id: string;
18
5
  type: FieldType;
@@ -22,7 +9,6 @@ export interface BaseField {
22
9
  maxLength?: number;
23
10
  }
24
11
 
25
- /** Text field */
26
12
  export interface TextField extends BaseField {
27
13
  type: "text";
28
14
  placeholder?: string;
@@ -32,7 +18,6 @@ export interface TextField extends BaseField {
32
18
  maxLength?: number;
33
19
  }
34
20
 
35
- /** Textarea field */
36
21
  export interface TextareaField extends BaseField {
37
22
  type: "textarea";
38
23
  placeholder?: string;
@@ -42,14 +27,12 @@ export interface TextareaField extends BaseField {
42
27
  defaultValue?: string;
43
28
  }
44
29
 
45
- /** Radio field */
46
30
  export interface RadioField extends BaseField {
47
31
  type: "radio";
48
32
  choices: string[];
49
33
  defaultValue?: string;
50
34
  }
51
35
 
52
- /** Dropdown field */
53
36
  export interface DropdownField extends BaseField {
54
37
  type: "dropdown";
55
38
  choices: string[];
@@ -57,7 +40,6 @@ export interface DropdownField extends BaseField {
57
40
  defaultValue?: string;
58
41
  }
59
42
 
60
- /** Checkbox field */
61
43
  export interface CheckboxField extends BaseField {
62
44
  type: "checkbox";
63
45
  choices: string[];
@@ -66,7 +48,6 @@ export interface CheckboxField extends BaseField {
66
48
  defaultValue?: string[];
67
49
  }
68
50
 
69
- /** Date field */
70
51
  export interface DateField extends BaseField {
71
52
  type: "date";
72
53
  minDate?: string;
@@ -75,14 +56,12 @@ export interface DateField extends BaseField {
75
56
  defaultValue?: string;
76
57
  }
77
58
 
78
- /** Time field */
79
59
  export interface TimeField extends BaseField {
80
60
  type: "time";
81
61
  format?: "12hr" | "24hr";
82
62
  defaultValue?: string;
83
63
  }
84
64
 
85
- /** Number field */
86
65
  export interface NumberField extends BaseField {
87
66
  type: "number";
88
67
  min?: number;
@@ -91,17 +70,14 @@ export interface NumberField extends BaseField {
91
70
  defaultValue?: number;
92
71
  }
93
72
 
94
- /** Union type for all fields */
95
73
  export type FormField = TextField | TextareaField | RadioField | DropdownField | CheckboxField | DateField | TimeField | NumberField;
96
74
 
97
- /** Form data stored in result.jsonData */
98
75
  export interface FormData {
99
76
  title?: string;
100
77
  description?: string;
101
78
  fields: FormField[];
102
79
  }
103
80
 
104
- /** Arguments passed to the form tool */
105
81
  export interface FormArgs {
106
82
  title?: string;
107
83
  description?: string;
@@ -1,11 +1,8 @@
1
1
  <template>
2
2
  <div class="p-2 text-sm">
3
- <div class="font-medium text-gray-700 truncate mb-1">
3
+ <div class="font-medium text-gray-700 truncate">
4
4
  {{ title }}
5
5
  </div>
6
- <div v-if="hint" class="text-xs text-gray-500 leading-relaxed truncate">
7
- {{ hint }}
8
- </div>
9
6
  </div>
10
7
  </template>
11
8
 
@@ -13,13 +10,9 @@
13
10
  import { computed } from "vue";
14
11
  import type { ToolResultComplete } from "gui-chat-protocol/vue";
15
12
  import type { PresentHtmlData } from "./index";
16
- import { stripHtmlToPreview } from "./helpers";
17
-
18
- const HINT_MAX_LENGTH = 60;
19
13
 
20
14
  const props = defineProps<{ result: ToolResultComplete<PresentHtmlData> }>();
21
15
 
22
16
  const data = computed(() => props.result.data);
23
17
  const title = computed(() => data.value?.title ?? "HTML Page");
24
- const hint = computed(() => stripHtmlToPreview(data.value?.html ?? "", HINT_MAX_LENGTH));
25
18
  </script>