mulmoclaude 0.1.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 (408) hide show
  1. package/README.md +44 -0
  2. package/bin/mulmoclaude.js +202 -0
  3. package/bin/prepare-dist.js +93 -0
  4. package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +1 -0
  5. package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +5 -0
  6. package/client/assets/index-D8rhwXLq.js +4906 -0
  7. package/client/assets/index-KNLBjwuh.css +1 -0
  8. package/client/assets/index.es-D4YyL_Dg-BfRHLTZV.js +5 -0
  9. package/client/assets/material-icons-Dr0goTwe.woff +0 -0
  10. package/client/assets/material-icons-kAwBdRge.woff2 +0 -0
  11. package/client/assets/material-icons-outlined-BpWbwl2n.woff +0 -0
  12. package/client/assets/material-icons-outlined-DZhiGvEA.woff2 +0 -0
  13. package/client/assets/material-icons-round-BDlwx-sv.woff +0 -0
  14. package/client/assets/material-icons-round-DrirKXBx.woff2 +0 -0
  15. package/client/assets/material-icons-sharp-CH1KkVu7.woff +0 -0
  16. package/client/assets/material-icons-sharp-gidztirS.woff2 +0 -0
  17. package/client/assets/material-icons-two-tone-B7wz7mED.woff +0 -0
  18. package/client/assets/material-icons-two-tone-DuNIpaEj.woff2 +0 -0
  19. package/client/assets/mulmo_bw-ERmkSv0a.png +0 -0
  20. package/client/assets/purify.es-Fx1Nqyry-PeS5RUhs.js +2 -0
  21. package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +1 -0
  22. package/client/index.html +28 -0
  23. package/package.json +66 -0
  24. package/server/agent/attachmentConverter.ts +270 -0
  25. package/server/agent/config.ts +414 -0
  26. package/server/agent/index.ts +260 -0
  27. package/server/agent/mcp-server.ts +412 -0
  28. package/server/agent/mcp-tools/index.ts +63 -0
  29. package/server/agent/mcp-tools/x.ts +188 -0
  30. package/server/agent/plugin-names.ts +75 -0
  31. package/server/agent/prompt.ts +349 -0
  32. package/server/agent/resumeFailover.ts +129 -0
  33. package/server/agent/sandboxMounts.ts +329 -0
  34. package/server/agent/stream.ts +194 -0
  35. package/server/api/auth/bearerAuth.ts +61 -0
  36. package/server/api/auth/token.ts +98 -0
  37. package/server/api/csrfGuard.ts +85 -0
  38. package/server/api/routes/agent.ts +478 -0
  39. package/server/api/routes/chart.ts +98 -0
  40. package/server/api/routes/chat-index.ts +46 -0
  41. package/server/api/routes/config.ts +258 -0
  42. package/server/api/routes/dispatchResponse.ts +79 -0
  43. package/server/api/routes/files.ts +812 -0
  44. package/server/api/routes/html.ts +101 -0
  45. package/server/api/routes/image.ts +169 -0
  46. package/server/api/routes/mulmo-script.ts +712 -0
  47. package/server/api/routes/mulmoScriptValidate.ts +101 -0
  48. package/server/api/routes/notifications.ts +69 -0
  49. package/server/api/routes/pdf.ts +163 -0
  50. package/server/api/routes/plugins.ts +276 -0
  51. package/server/api/routes/presentHtml.ts +48 -0
  52. package/server/api/routes/roles.ts +125 -0
  53. package/server/api/routes/scheduler.ts +153 -0
  54. package/server/api/routes/schedulerHandlers.ts +151 -0
  55. package/server/api/routes/schedulerTasks.ts +163 -0
  56. package/server/api/routes/sessions.ts +294 -0
  57. package/server/api/routes/sessionsCursor.ts +59 -0
  58. package/server/api/routes/skills.ts +195 -0
  59. package/server/api/routes/sources.ts +540 -0
  60. package/server/api/routes/todos.ts +263 -0
  61. package/server/api/routes/todosColumnsHandlers.ts +347 -0
  62. package/server/api/routes/todosHandlers.ts +274 -0
  63. package/server/api/routes/todosItemsHandlers.ts +386 -0
  64. package/server/api/routes/wiki/pageIndex.ts +53 -0
  65. package/server/api/routes/wiki.ts +363 -0
  66. package/server/api/sandboxStatus.ts +64 -0
  67. package/server/events/notifications.ts +160 -0
  68. package/server/events/pub-sub/index.ts +45 -0
  69. package/server/events/relay-client.ts +288 -0
  70. package/server/events/scheduler-adapter.ts +302 -0
  71. package/server/events/session-store/index.ts +492 -0
  72. package/server/events/task-manager/index.ts +181 -0
  73. package/server/index.ts +572 -0
  74. package/server/system/config.ts +243 -0
  75. package/server/system/credentials.ts +220 -0
  76. package/server/system/docker.ts +97 -0
  77. package/server/system/env.ts +109 -0
  78. package/server/system/logger/config.ts +112 -0
  79. package/server/system/logger/formatters.ts +40 -0
  80. package/server/system/logger/index.ts +53 -0
  81. package/server/system/logger/rotation.ts +37 -0
  82. package/server/system/logger/sinks.ts +101 -0
  83. package/server/system/logger/types.ts +29 -0
  84. package/server/utils/date.ts +57 -0
  85. package/server/utils/errors.ts +7 -0
  86. package/server/utils/fetch.ts +27 -0
  87. package/server/utils/files/atomic.ts +125 -0
  88. package/server/utils/files/html-io.ts +20 -0
  89. package/server/utils/files/image-store.ts +66 -0
  90. package/server/utils/files/index.ts +45 -0
  91. package/server/utils/files/journal-io.ts +213 -0
  92. package/server/utils/files/json.ts +69 -0
  93. package/server/utils/files/markdown-store.ts +33 -0
  94. package/server/utils/files/naming.ts +50 -0
  95. package/server/utils/files/reference-dirs-io.ts +45 -0
  96. package/server/utils/files/roles-io.ts +45 -0
  97. package/server/utils/files/safe.ts +106 -0
  98. package/server/utils/files/scheduler-io.ts +20 -0
  99. package/server/utils/files/scheduler-overrides-io.ts +64 -0
  100. package/server/utils/files/session-io.ts +136 -0
  101. package/server/utils/files/spreadsheet-store.ts +63 -0
  102. package/server/utils/files/todos-io.ts +29 -0
  103. package/server/utils/files/user-tasks-io.ts +25 -0
  104. package/server/utils/files/workspace-io.ts +221 -0
  105. package/server/utils/gemini.ts +59 -0
  106. package/server/utils/gitignore.ts +69 -0
  107. package/server/utils/http.ts +15 -0
  108. package/server/utils/httpError.ts +61 -0
  109. package/server/utils/id.ts +16 -0
  110. package/server/utils/json.ts +83 -0
  111. package/server/utils/logBackgroundError.ts +22 -0
  112. package/server/utils/markdown.ts +82 -0
  113. package/server/utils/request.ts +29 -0
  114. package/server/utils/slug.ts +50 -0
  115. package/server/utils/spawn.ts +62 -0
  116. package/server/utils/time.ts +34 -0
  117. package/server/utils/types.ts +47 -0
  118. package/server/workspace/chat-index/index.ts +153 -0
  119. package/server/workspace/chat-index/indexer.ts +209 -0
  120. package/server/workspace/chat-index/paths.ts +34 -0
  121. package/server/workspace/chat-index/summarizer.ts +247 -0
  122. package/server/workspace/chat-index/types.ts +38 -0
  123. package/server/workspace/custom-dirs.ts +220 -0
  124. package/server/workspace/helps/business.md +104 -0
  125. package/server/workspace/helps/github.md +23 -0
  126. package/server/workspace/helps/index.md +60 -0
  127. package/server/workspace/helps/mulmoscript.md +249 -0
  128. package/server/workspace/helps/sandbox.md +90 -0
  129. package/server/workspace/helps/spreadsheet.md +43 -0
  130. package/server/workspace/helps/telegram.md +135 -0
  131. package/server/workspace/helps/wiki.md +131 -0
  132. package/server/workspace/journal/archivist.ts +386 -0
  133. package/server/workspace/journal/dailyPass.ts +743 -0
  134. package/server/workspace/journal/diff.ts +71 -0
  135. package/server/workspace/journal/index.ts +185 -0
  136. package/server/workspace/journal/indexFile.ts +136 -0
  137. package/server/workspace/journal/linkRewrite.ts +4 -0
  138. package/server/workspace/journal/memoryExtractor.ts +130 -0
  139. package/server/workspace/journal/optimizationPass.ts +160 -0
  140. package/server/workspace/journal/paths.ts +76 -0
  141. package/server/workspace/journal/state.ts +125 -0
  142. package/server/workspace/paths.ts +158 -0
  143. package/server/workspace/reference-dirs.ts +252 -0
  144. package/server/workspace/roles.ts +37 -0
  145. package/server/workspace/skills/discovery.ts +125 -0
  146. package/server/workspace/skills/index.ts +10 -0
  147. package/server/workspace/skills/parser.ts +144 -0
  148. package/server/workspace/skills/paths.ts +41 -0
  149. package/server/workspace/skills/scheduler.ts +149 -0
  150. package/server/workspace/skills/types.ts +30 -0
  151. package/server/workspace/skills/user-tasks.ts +257 -0
  152. package/server/workspace/skills/writer.ts +189 -0
  153. package/server/workspace/sources/arxivDiscovery.ts +182 -0
  154. package/server/workspace/sources/classifier.ts +268 -0
  155. package/server/workspace/sources/fetchers/arxiv.ts +170 -0
  156. package/server/workspace/sources/fetchers/github.ts +106 -0
  157. package/server/workspace/sources/fetchers/githubIssues.ts +208 -0
  158. package/server/workspace/sources/fetchers/githubReleases.ts +186 -0
  159. package/server/workspace/sources/fetchers/index.ts +71 -0
  160. package/server/workspace/sources/fetchers/registerAll.ts +15 -0
  161. package/server/workspace/sources/fetchers/rss.ts +141 -0
  162. package/server/workspace/sources/fetchers/rssParser.ts +295 -0
  163. package/server/workspace/sources/httpFetcher.ts +230 -0
  164. package/server/workspace/sources/interests.ts +120 -0
  165. package/server/workspace/sources/paths.ts +110 -0
  166. package/server/workspace/sources/pipeline/dedup.ts +60 -0
  167. package/server/workspace/sources/pipeline/fetch.ts +136 -0
  168. package/server/workspace/sources/pipeline/index.ts +249 -0
  169. package/server/workspace/sources/pipeline/notify.ts +72 -0
  170. package/server/workspace/sources/pipeline/plan.ts +66 -0
  171. package/server/workspace/sources/pipeline/summarize.ts +189 -0
  172. package/server/workspace/sources/pipeline/write.ts +185 -0
  173. package/server/workspace/sources/rateLimiter.ts +148 -0
  174. package/server/workspace/sources/registry.ts +326 -0
  175. package/server/workspace/sources/robots.ts +271 -0
  176. package/server/workspace/sources/sourceState.ts +135 -0
  177. package/server/workspace/sources/taxonomy.ts +74 -0
  178. package/server/workspace/sources/types.ts +144 -0
  179. package/server/workspace/sources/urls.ts +112 -0
  180. package/server/workspace/tool-trace/classify.ts +114 -0
  181. package/server/workspace/tool-trace/index.ts +250 -0
  182. package/server/workspace/tool-trace/writeSearch.ts +98 -0
  183. package/server/workspace/wiki-backlinks/index.ts +107 -0
  184. package/server/workspace/wiki-backlinks/sessionBacklinks.ts +144 -0
  185. package/server/workspace/workspace.ts +66 -0
  186. package/src/App.vue +720 -0
  187. package/src/assets/mulmo_bw.png +0 -0
  188. package/src/components/CanvasViewToggle.vue +27 -0
  189. package/src/components/ChatAttachmentPreview.vue +45 -0
  190. package/src/components/ChatImagePreview.vue +17 -0
  191. package/src/components/ChatInput.vue +208 -0
  192. package/src/components/FileContentHeader.vue +49 -0
  193. package/src/components/FileContentRenderer.vue +162 -0
  194. package/src/components/FileTree.vue +115 -0
  195. package/src/components/FileTreePane.vue +85 -0
  196. package/src/components/FilesView.vue +206 -0
  197. package/src/components/LockStatusPopup.vue +111 -0
  198. package/src/components/NotificationBell.vue +131 -0
  199. package/src/components/NotificationToast.vue +72 -0
  200. package/src/components/PluginLauncher.vue +138 -0
  201. package/src/components/RightSidebar.vue +113 -0
  202. package/src/components/RoleSelector.vue +64 -0
  203. package/src/components/SessionHistoryPanel.vue +176 -0
  204. package/src/components/SessionTabBar.vue +81 -0
  205. package/src/components/SettingsMcpTab.vue +350 -0
  206. package/src/components/SettingsModal.vue +275 -0
  207. package/src/components/SettingsReferenceDirsTab.vue +173 -0
  208. package/src/components/SettingsWorkspaceDirsTab.vue +174 -0
  209. package/src/components/SidebarHeader.vue +69 -0
  210. package/src/components/StackView.vue +360 -0
  211. package/src/components/SuggestionsPanel.vue +65 -0
  212. package/src/components/TodoExplorer.vue +358 -0
  213. package/src/components/ToolResultsPanel.vue +77 -0
  214. package/src/components/todo/TodoAddDialog.vue +131 -0
  215. package/src/components/todo/TodoEditDialog.vue +47 -0
  216. package/src/components/todo/TodoEditPanel.vue +113 -0
  217. package/src/components/todo/TodoKanbanView.vue +249 -0
  218. package/src/components/todo/TodoListView.vue +79 -0
  219. package/src/components/todo/TodoTableView.vue +177 -0
  220. package/src/composables/useActiveSession.ts +40 -0
  221. package/src/composables/useAppApi.ts +45 -0
  222. package/src/composables/useCanvasViewMode.ts +121 -0
  223. package/src/composables/useChatScroll.ts +47 -0
  224. package/src/composables/useClickOutside.ts +26 -0
  225. package/src/composables/useClipboardCopy.ts +44 -0
  226. package/src/composables/useContentDisplay.ts +52 -0
  227. package/src/composables/useDebugBeat.ts +23 -0
  228. package/src/composables/useDynamicFavicon.ts +115 -0
  229. package/src/composables/useEventListeners.ts +42 -0
  230. package/src/composables/useExpandedDirs.ts +64 -0
  231. package/src/composables/useFaviconState.ts +30 -0
  232. package/src/composables/useFileSelection.ts +115 -0
  233. package/src/composables/useFileSortMode.ts +24 -0
  234. package/src/composables/useFileTree.ts +85 -0
  235. package/src/composables/useFreshPluginData.ts +89 -0
  236. package/src/composables/useHealth.ts +38 -0
  237. package/src/composables/useImeAwareEnter.ts +57 -0
  238. package/src/composables/useKeyNavigation.ts +60 -0
  239. package/src/composables/useMarkdownLinkHandler.ts +46 -0
  240. package/src/composables/useMarkdownMode.ts +17 -0
  241. package/src/composables/useMcpTools.ts +71 -0
  242. package/src/composables/useMergedSessions.ts +27 -0
  243. package/src/composables/useNotifications.ts +90 -0
  244. package/src/composables/usePdfDownload.ts +60 -0
  245. package/src/composables/usePendingCalls.ts +77 -0
  246. package/src/composables/usePubSub.ts +85 -0
  247. package/src/composables/useRightSidebar.ts +23 -0
  248. package/src/composables/useRoles.ts +34 -0
  249. package/src/composables/useSandboxStatus.ts +67 -0
  250. package/src/composables/useSelectedResult.ts +49 -0
  251. package/src/composables/useSessionDerived.ts +51 -0
  252. package/src/composables/useSessionHistory.ts +81 -0
  253. package/src/composables/useSessionSync.ts +57 -0
  254. package/src/composables/useViewLayout.ts +55 -0
  255. package/src/config/apiRoutes.ts +173 -0
  256. package/src/config/pubsubChannels.ts +45 -0
  257. package/src/config/roles.ts +335 -0
  258. package/src/config/schedulerActions.ts +25 -0
  259. package/src/config/toolNames.ts +71 -0
  260. package/src/config/workspacePaths.ts +24 -0
  261. package/src/index.css +107 -0
  262. package/src/main.ts +25 -0
  263. package/src/plugins/canvas/Preview.vue +13 -0
  264. package/src/plugins/canvas/View.vue +333 -0
  265. package/src/plugins/canvas/definition.ts +38 -0
  266. package/src/plugins/canvas/index.ts +36 -0
  267. package/src/plugins/chart/Preview.vue +49 -0
  268. package/src/plugins/chart/View.vue +143 -0
  269. package/src/plugins/chart/definition.ts +58 -0
  270. package/src/plugins/chart/index.ts +52 -0
  271. package/src/plugins/editImage/Preview.vue +13 -0
  272. package/src/plugins/editImage/View.vue +13 -0
  273. package/src/plugins/editImage/definition.ts +27 -0
  274. package/src/plugins/editImage/index.ts +36 -0
  275. package/src/plugins/generateImage/Preview.vue +13 -0
  276. package/src/plugins/generateImage/View.vue +33 -0
  277. package/src/plugins/generateImage/definition.ts +32 -0
  278. package/src/plugins/generateImage/index.ts +56 -0
  279. package/src/plugins/manageRoles/Preview.vue +49 -0
  280. package/src/plugins/manageRoles/View.vue +525 -0
  281. package/src/plugins/manageRoles/definition.ts +43 -0
  282. package/src/plugins/manageRoles/index.ts +47 -0
  283. package/src/plugins/manageSkills/Preview.vue +21 -0
  284. package/src/plugins/manageSkills/View.vue +321 -0
  285. package/src/plugins/manageSkills/definition.ts +49 -0
  286. package/src/plugins/manageSkills/index.ts +49 -0
  287. package/src/plugins/manageSource/Preview.vue +33 -0
  288. package/src/plugins/manageSource/View.vue +697 -0
  289. package/src/plugins/manageSource/definition.ts +63 -0
  290. package/src/plugins/manageSource/index.ts +66 -0
  291. package/src/plugins/markdown/Preview.vue +77 -0
  292. package/src/plugins/markdown/View.vue +476 -0
  293. package/src/plugins/markdown/definition.ts +50 -0
  294. package/src/plugins/markdown/index.ts +36 -0
  295. package/src/plugins/presentHtml/Preview.vue +25 -0
  296. package/src/plugins/presentHtml/View.vue +52 -0
  297. package/src/plugins/presentHtml/definition.ts +27 -0
  298. package/src/plugins/presentHtml/helpers.ts +72 -0
  299. package/src/plugins/presentHtml/index.ts +41 -0
  300. package/src/plugins/presentMulmoScript/Preview.vue +23 -0
  301. package/src/plugins/presentMulmoScript/View.vue +1166 -0
  302. package/src/plugins/presentMulmoScript/definition.ts +95 -0
  303. package/src/plugins/presentMulmoScript/helpers.ts +162 -0
  304. package/src/plugins/presentMulmoScript/index.ts +40 -0
  305. package/src/plugins/scheduler/Preview.vue +67 -0
  306. package/src/plugins/scheduler/TasksTab.vue +205 -0
  307. package/src/plugins/scheduler/View.vue +565 -0
  308. package/src/plugins/scheduler/definition.ts +57 -0
  309. package/src/plugins/scheduler/index.ts +45 -0
  310. package/src/plugins/scheduler/viewModes.ts +26 -0
  311. package/src/plugins/spreadsheet/Preview.vue +29 -0
  312. package/src/plugins/spreadsheet/View.vue +997 -0
  313. package/src/plugins/spreadsheet/cellHighlights.ts +79 -0
  314. package/src/plugins/spreadsheet/definition.ts +121 -0
  315. package/src/plugins/spreadsheet/engine/calculator.ts +459 -0
  316. package/src/plugins/spreadsheet/engine/cellBuilder.ts +81 -0
  317. package/src/plugins/spreadsheet/engine/date-parser.ts +220 -0
  318. package/src/plugins/spreadsheet/engine/date-utils.ts +56 -0
  319. package/src/plugins/spreadsheet/engine/engine.ts +176 -0
  320. package/src/plugins/spreadsheet/engine/evaluator.ts +390 -0
  321. package/src/plugins/spreadsheet/engine/formatter.ts +172 -0
  322. package/src/plugins/spreadsheet/engine/formulaRefs.ts +101 -0
  323. package/src/plugins/spreadsheet/engine/functions/date.ts +299 -0
  324. package/src/plugins/spreadsheet/engine/functions/financial.ts +387 -0
  325. package/src/plugins/spreadsheet/engine/functions/index.ts +16 -0
  326. package/src/plugins/spreadsheet/engine/functions/logical.ts +262 -0
  327. package/src/plugins/spreadsheet/engine/functions/lookup.ts +400 -0
  328. package/src/plugins/spreadsheet/engine/functions/mathematical.ts +297 -0
  329. package/src/plugins/spreadsheet/engine/functions/statistical.ts +338 -0
  330. package/src/plugins/spreadsheet/engine/functions/text.ts +389 -0
  331. package/src/plugins/spreadsheet/engine/index.ts +27 -0
  332. package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +111 -0
  333. package/src/plugins/spreadsheet/engine/parser.ts +143 -0
  334. package/src/plugins/spreadsheet/engine/registry.ts +150 -0
  335. package/src/plugins/spreadsheet/engine/responseDecoder.ts +67 -0
  336. package/src/plugins/spreadsheet/engine/types.ts +64 -0
  337. package/src/plugins/spreadsheet/index.ts +36 -0
  338. package/src/plugins/textResponse/Preview.vue +94 -0
  339. package/src/plugins/textResponse/View.vue +503 -0
  340. package/src/plugins/textResponse/definition.ts +34 -0
  341. package/src/plugins/textResponse/index.ts +27 -0
  342. package/src/plugins/textResponse/plugin.ts +29 -0
  343. package/src/plugins/textResponse/samples.ts +97 -0
  344. package/src/plugins/textResponse/types.ts +11 -0
  345. package/src/plugins/todo/Preview.vue +63 -0
  346. package/src/plugins/todo/View.vue +364 -0
  347. package/src/plugins/todo/composables/useTodos.ts +177 -0
  348. package/src/plugins/todo/definition.ts +45 -0
  349. package/src/plugins/todo/index.ts +61 -0
  350. package/src/plugins/todo/labels.ts +163 -0
  351. package/src/plugins/todo/priority.ts +98 -0
  352. package/src/plugins/todo/viewModes.ts +19 -0
  353. package/src/plugins/ui-image/ImagePreview.vue +23 -0
  354. package/src/plugins/ui-image/ImageView.vue +34 -0
  355. package/src/plugins/ui-image/index.ts +3 -0
  356. package/src/plugins/ui-image/types.ts +4 -0
  357. package/src/plugins/wiki/Preview.vue +65 -0
  358. package/src/plugins/wiki/View.vue +342 -0
  359. package/src/plugins/wiki/definition.ts +25 -0
  360. package/src/plugins/wiki/helpers.ts +59 -0
  361. package/src/plugins/wiki/index.ts +52 -0
  362. package/src/router/guards.ts +61 -0
  363. package/src/router/index.ts +50 -0
  364. package/src/tools/index.ts +52 -0
  365. package/src/tools/types.ts +27 -0
  366. package/src/types/events.ts +16 -0
  367. package/src/types/fileTree.ts +13 -0
  368. package/src/types/notification.ts +67 -0
  369. package/src/types/session.ts +116 -0
  370. package/src/types/sse.ts +90 -0
  371. package/src/types/toolCallHistory.ts +13 -0
  372. package/src/utils/agent/eventDispatch.ts +74 -0
  373. package/src/utils/agent/request.ts +55 -0
  374. package/src/utils/agent/toolCalls.ts +62 -0
  375. package/src/utils/api.ts +218 -0
  376. package/src/utils/canvas/viewMode.ts +46 -0
  377. package/src/utils/dom/authTokenMeta.ts +20 -0
  378. package/src/utils/dom/clickOutside.ts +11 -0
  379. package/src/utils/dom/externalLink.ts +57 -0
  380. package/src/utils/dom/scrollable.ts +24 -0
  381. package/src/utils/errors.ts +11 -0
  382. package/src/utils/files/expandedDirs.ts +25 -0
  383. package/src/utils/files/filename.ts +12 -0
  384. package/src/utils/files/sortChildren.ts +20 -0
  385. package/src/utils/filesPreview/schedulerPreview.ts +38 -0
  386. package/src/utils/filesPreview/todoPreview.ts +40 -0
  387. package/src/utils/format/date.ts +85 -0
  388. package/src/utils/format/frontmatter.ts +80 -0
  389. package/src/utils/format/jsonSyntax.ts +109 -0
  390. package/src/utils/html/previewCsp.ts +65 -0
  391. package/src/utils/image/resolve.ts +8 -0
  392. package/src/utils/image/rewriteMarkdownImageRefs.ts +182 -0
  393. package/src/utils/markdown/extractFirstH1.ts +39 -0
  394. package/src/utils/notification/dispatch.ts +22 -0
  395. package/src/utils/path/relativeLink.ts +130 -0
  396. package/src/utils/role/icon.ts +20 -0
  397. package/src/utils/role/merge.ts +10 -0
  398. package/src/utils/role/plugins.ts +12 -0
  399. package/src/utils/session/mergeSessions.ts +103 -0
  400. package/src/utils/session/seedRoleDefault.ts +35 -0
  401. package/src/utils/session/sessionEntries.ts +121 -0
  402. package/src/utils/session/sessionFactory.ts +22 -0
  403. package/src/utils/session/sessionHelpers.ts +99 -0
  404. package/src/utils/tools/dedup.ts +17 -0
  405. package/src/utils/tools/mcp.ts +33 -0
  406. package/src/utils/tools/pendingCalls.ts +16 -0
  407. package/src/utils/tools/result.ts +40 -0
  408. package/src/utils/types.ts +44 -0
@@ -0,0 +1,360 @@
1
+ <template>
2
+ <div ref="containerRef" class="h-full overflow-y-auto bg-gray-50 p-4 space-y-3" data-testid="stack-scroll">
3
+ <div v-if="toolResults.length === 0" class="flex items-center justify-center h-full text-gray-400 text-sm">No results yet</div>
4
+ <div
5
+ v-for="result in toolResults"
6
+ :key="result.uuid"
7
+ :ref="(element) => setItemRef(result.uuid, element as HTMLElement | null)"
8
+ class="bg-white rounded-lg border transition-colors"
9
+ :class="result.uuid === selectedResultUuid ? 'border-blue-400 ring-2 ring-blue-200' : 'border-gray-200'"
10
+ >
11
+ <button
12
+ class="w-full flex items-center gap-2 px-3 py-2 border-b border-gray-100 text-left hover:bg-gray-50"
13
+ :title="result.title || result.toolName"
14
+ @click="emit('select', result.uuid)"
15
+ >
16
+ <span class="material-icons text-sm text-gray-400">{{ iconFor(result.toolName) }}</span>
17
+ <span class="text-sm font-medium text-gray-800 truncate">{{ result.title || result.toolName }}</span>
18
+ <span v-if="resultTimestamps.get(result.uuid)" class="text-[10px] text-gray-400 shrink-0">{{
19
+ formatSmartTime(resultTimestamps.get(result.uuid)!)
20
+ }}</span>
21
+ <span class="font-mono text-xs text-gray-400 shrink-0">{{ result.toolName }}</span>
22
+ </button>
23
+ <!-- text-response: render the message as Markdown via the
24
+ underlying plugin View. The .stack-text-response class below
25
+ collapses the plugin's own card chrome (outer p-6, inner
26
+ rounded/border/shadow box, role header) so only the stack
27
+ card's own border shows.
28
+
29
+ We render the upstream OriginalView directly rather than our
30
+ local TextResponseView wrapper, so we lose the wrapper's
31
+ "open external links in a new tab" click handler. Attach
32
+ the same handler here via @click.capture so cross-origin
33
+ links in assistant Markdown don't navigate the SPA away. -->
34
+ <div v-if="isTextResponse(result)" class="stack-text-response" @click.capture="handleExternalLinkClick">
35
+ <TextResponseOriginalView :selected-result="result" />
36
+ </div>
37
+ <!-- Document-like plugins: let the content flow at its natural
38
+ height by overriding the plugin's internal h-full / overflow
39
+ / flex-1 via the .stack-natural scoped styles below. For
40
+ plugins that embed iframes (e.g. presentHtml) we also size
41
+ each iframe to its content after load. -->
42
+ <div
43
+ v-else-if="isStackNatural(result.toolName)"
44
+ :ref="(element) => setNaturalWrapperRef(result.uuid, element as HTMLElement | null)"
45
+ class="stack-natural"
46
+ >
47
+ <component
48
+ :is="getPlugin(result.toolName)?.viewComponent"
49
+ v-if="getPlugin(result.toolName)?.viewComponent"
50
+ :selected-result="result"
51
+ :send-text-message="sendTextMessage"
52
+ @update-result="(r: ToolResultComplete) => emit('updateResult', r)"
53
+ />
54
+ </div>
55
+ <!-- Other plugins: fixed height wrapper so plugins that rely on
56
+ h-full continue to render properly. -->
57
+ <div v-else :style="{ height: PLUGIN_HEIGHT }">
58
+ <component
59
+ :is="getPlugin(result.toolName)?.viewComponent"
60
+ v-if="getPlugin(result.toolName)?.viewComponent"
61
+ :selected-result="result"
62
+ :send-text-message="sendTextMessage"
63
+ @update-result="(r: ToolResultComplete) => emit('updateResult', r)"
64
+ />
65
+ <pre v-else class="h-full overflow-auto p-4 text-xs text-gray-500 whitespace-pre-wrap">{{ JSON.stringify(result, null, 2) }}</pre>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </template>
70
+
71
+ <script setup lang="ts">
72
+ import { ref, computed, watch, nextTick, onMounted, onUnmounted } from "vue";
73
+ import { getPlugin } from "../tools";
74
+ import type { ToolResultComplete } from "gui-chat-protocol/vue";
75
+ import { View as TextResponseOriginalView } from "../plugins/textResponse/index";
76
+ import { handleExternalLinkClick } from "../utils/dom/externalLink";
77
+ import type { TextResponseData } from "../plugins/textResponse/types";
78
+ import { formatSmartTime } from "../utils/format/date";
79
+ import { isRecord } from "../utils/types";
80
+
81
+ // Most plugin viewComponents use h-full internally, so a defined parent
82
+ // height is required for them to render. text-response and the
83
+ // "stack-natural" plugins below are special-cased.
84
+ const PLUGIN_HEIGHT = "min(60vh, 560px)";
85
+
86
+ // How long to ignore scroll-spy after a programmatic scroll (sidebar
87
+ // click, auto-scroll on new result). Keeps the spy from emitting a
88
+ // stale uuid while the scroll is still settling.
89
+ const SCROLL_SPY_SUPPRESS_MS = 150;
90
+
91
+ // Plugins that look better flowing at natural height in stack view
92
+ // rather than being clipped to PLUGIN_HEIGHT with an inner scrollbar.
93
+ const STACK_NATURAL_TOOLS = new Set<string>([
94
+ "presentHtml",
95
+ "presentDocument",
96
+ "presentSpreadsheet",
97
+ "manageWiki",
98
+ // presentChart documents can hold multiple charts; fixed-height
99
+ // clipping forces an inner scrollbar per result. Letting them flow
100
+ // keeps everything visible in one scroll.
101
+ "presentChart",
102
+ ]);
103
+
104
+ function isStackNatural(toolName: string): boolean {
105
+ return STACK_NATURAL_TOOLS.has(toolName);
106
+ }
107
+
108
+ const props = defineProps<{
109
+ toolResults: ToolResultComplete[];
110
+ selectedResultUuid: string | null;
111
+ resultTimestamps: Map<string, number>;
112
+ sendTextMessage?: (text: string) => void;
113
+ }>();
114
+
115
+ const emit = defineEmits<{
116
+ select: [uuid: string];
117
+ updateResult: [result: ToolResultComplete];
118
+ }>();
119
+
120
+ const containerRef = ref<HTMLDivElement | null>(null);
121
+ const itemRefs = new Map<string, HTMLElement>();
122
+ const naturalWrapperRefs = new Map<string, HTMLElement>();
123
+
124
+ function setItemRef(uuid: string, element: HTMLElement | null): void {
125
+ if (element) itemRefs.set(uuid, element);
126
+ else itemRefs.delete(uuid);
127
+ }
128
+
129
+ function setNaturalWrapperRef(uuid: string, element: HTMLElement | null): void {
130
+ if (element) {
131
+ naturalWrapperRefs.set(uuid, element);
132
+ nextTick(() => sizeIframesIn(element));
133
+ } else {
134
+ naturalWrapperRefs.delete(uuid);
135
+ }
136
+ }
137
+
138
+ // Sandboxed iframes inside stack-natural plugins (e.g. presentHtml)
139
+ // have no intrinsic content height, so CSS alone collapses them. Set
140
+ // each iframe's height to match its document's scrollHeight on load.
141
+ function sizeIframesIn(wrapper: HTMLElement): void {
142
+ const iframes = wrapper.querySelectorAll<HTMLIFrameElement>("iframe");
143
+ for (const iframe of iframes) {
144
+ if (iframe.dataset.stackSized === "true") continue;
145
+ iframe.dataset.stackSized = "true";
146
+ const resize = () => resizeOneIframe(iframe);
147
+ iframe.addEventListener("load", resize);
148
+ // If the iframe already finished loading before we attached the
149
+ // listener, size it now as well.
150
+ try {
151
+ if (iframe.contentDocument?.readyState === "complete") {
152
+ resize();
153
+ }
154
+ } catch {
155
+ // cross-origin — leave default height
156
+ }
157
+ }
158
+ }
159
+
160
+ function resizeOneIframe(iframe: HTMLIFrameElement): void {
161
+ try {
162
+ const doc = iframe.contentDocument;
163
+ if (!doc) return;
164
+ const height = Math.max(doc.documentElement?.scrollHeight ?? 0, doc.body?.scrollHeight ?? 0);
165
+ if (height > 0) iframe.style.height = `${height}px`;
166
+ } catch {
167
+ // cross-origin sandbox — can't measure, leave default
168
+ }
169
+ }
170
+
171
+ function isTextResponse(result: ToolResultComplete): result is ToolResultComplete<TextResponseData> {
172
+ if (result.toolName !== "text-response") return false;
173
+ const data = result.data;
174
+ if (!isRecord(data)) return false;
175
+ return typeof data.text === "string";
176
+ }
177
+
178
+ function iconFor(toolName: string): string {
179
+ if (toolName === "text-response") return "chat";
180
+ return "extension";
181
+ }
182
+
183
+ // Scroll-spy state: as the user scrolls the stack container we emit
184
+ // a `select` for whichever card currently occupies the top, so the
185
+ // sidebar selection always tracks what's on screen.
186
+ //
187
+ // Coordination between scroll and selection:
188
+ // - `suppressScrollSync` is set while the component programmatically
189
+ // scrolls (sidebar click → scrollIntoView, auto-scroll on new
190
+ // result) so the spy doesn't fire on its own scroll.
191
+ // - `scrollSpyEmittedUuid` holds the exact uuid the spy most
192
+ // recently emitted. The watch on `selectedResultUuid` only skips
193
+ // its scrollIntoView when the incoming uuid matches, so a
194
+ // sidebar click that arrives right after a spy emit still gets
195
+ // its normal scroll behaviour.
196
+ let suppressScrollSync = false;
197
+ let suppressScrollTimeout: ReturnType<typeof setTimeout> | null = null;
198
+ let scrollSpyRafId: number | null = null;
199
+ let scrollSpyEmittedUuid: string | null = null;
200
+
201
+ function beginSuppressScrollSync(): void {
202
+ suppressScrollSync = true;
203
+ if (suppressScrollTimeout !== null) clearTimeout(suppressScrollTimeout);
204
+ suppressScrollTimeout = setTimeout(() => {
205
+ suppressScrollSync = false;
206
+ suppressScrollTimeout = null;
207
+ }, SCROLL_SPY_SUPPRESS_MS);
208
+ }
209
+
210
+ function readPaddingTop(element: HTMLElement): number {
211
+ const value = parseFloat(getComputedStyle(element).paddingTop);
212
+ return Number.isFinite(value) ? value : 0;
213
+ }
214
+
215
+ // Walk items in order and return the last one whose top edge is at or
216
+ // above the padded content top of the container. Accounting for the
217
+ // container's padding-top means the handoff happens at the visual
218
+ // start of the cards rather than the invisible border of the
219
+ // container itself. Iterating in DOM order lets us break early once
220
+ // an item is below the line.
221
+ function computeActiveUuidFromScroll(): string | null {
222
+ if (!containerRef.value) return null;
223
+ const container = containerRef.value;
224
+ const paddedTop = container.getBoundingClientRect().top + readPaddingTop(container);
225
+ let activeUuid: string | null = null;
226
+ for (const result of props.toolResults) {
227
+ const element = itemRefs.get(result.uuid);
228
+ if (!element) continue;
229
+ if (element.getBoundingClientRect().top <= paddedTop) {
230
+ activeUuid = result.uuid;
231
+ } else {
232
+ break;
233
+ }
234
+ }
235
+ return activeUuid;
236
+ }
237
+
238
+ function onContainerScroll(): void {
239
+ if (suppressScrollSync) return;
240
+ if (scrollSpyRafId !== null) return;
241
+ scrollSpyRafId = requestAnimationFrame(() => {
242
+ scrollSpyRafId = null;
243
+ if (suppressScrollSync) return;
244
+ const activeUuid = computeActiveUuidFromScroll();
245
+ if (activeUuid && activeUuid !== props.selectedResultUuid) {
246
+ scrollSpyEmittedUuid = activeUuid;
247
+ emit("select", activeUuid);
248
+ }
249
+ });
250
+ }
251
+
252
+ // Scroll the selected card to the top whenever the external selection
253
+ // changes (sidebar click, initial load). Skip the scroll only when the
254
+ // incoming uuid matches the one we just emitted from the spy — that
255
+ // means the viewport is already in the right place. Any other change
256
+ // (sidebar click, new result) still gets its normal scrollIntoView.
257
+ watch(
258
+ () => props.selectedResultUuid,
259
+ (uuid) => {
260
+ if (!uuid) return;
261
+ if (scrollSpyEmittedUuid === uuid) {
262
+ scrollSpyEmittedUuid = null;
263
+ return;
264
+ }
265
+ scrollSpyEmittedUuid = null;
266
+ nextTick(() => {
267
+ const element = itemRefs.get(uuid);
268
+ if (!element) return;
269
+ beginSuppressScrollSync();
270
+ element.scrollIntoView({ block: "start", behavior: "auto" });
271
+ });
272
+ },
273
+ );
274
+
275
+ // Key that changes both on new results AND on streaming updates to
276
+ // the last text card (which appends in place, leaving length stable).
277
+ const latestResultScrollKey = computed(() => {
278
+ const list = props.toolResults;
279
+ const last = list[list.length - 1];
280
+ return `${list.length}:${last?.uuid ?? ""}:${last?.message?.length ?? 0}`;
281
+ });
282
+
283
+ watch(latestResultScrollKey, () => {
284
+ nextTick(() => {
285
+ if (containerRef.value) {
286
+ beginSuppressScrollSync();
287
+ containerRef.value.scrollTop = containerRef.value.scrollHeight;
288
+ }
289
+ // New items may have brought in more iframes to size.
290
+ for (const wrapper of naturalWrapperRefs.values()) {
291
+ sizeIframesIn(wrapper);
292
+ }
293
+ });
294
+ });
295
+
296
+ onMounted(() => {
297
+ containerRef.value?.addEventListener("scroll", onContainerScroll, {
298
+ passive: true,
299
+ });
300
+ // Align the initial scroll position with the externally selected
301
+ // item so the sidebar and stack start in sync on mount.
302
+ nextTick(() => {
303
+ if (!props.selectedResultUuid) return;
304
+ const element = itemRefs.get(props.selectedResultUuid);
305
+ if (!element) return;
306
+ beginSuppressScrollSync();
307
+ element.scrollIntoView({ block: "start", behavior: "auto" });
308
+ });
309
+ });
310
+
311
+ onUnmounted(() => {
312
+ containerRef.value?.removeEventListener("scroll", onContainerScroll);
313
+ if (scrollSpyRafId !== null) cancelAnimationFrame(scrollSpyRafId);
314
+ if (suppressScrollTimeout !== null) clearTimeout(suppressScrollTimeout);
315
+ naturalWrapperRefs.clear();
316
+ });
317
+ </script>
318
+
319
+ <style scoped>
320
+ /* Force document-like plugin viewComponents (presentHtml,
321
+ presentDocument, presentSpreadsheet) to flow at their natural
322
+ height inside stack view instead of clipping to the wrapper with
323
+ an inner scrollbar. */
324
+ .stack-natural :deep(.h-full),
325
+ .stack-natural :deep(.min-h-full) {
326
+ height: auto !important;
327
+ min-height: 0 !important;
328
+ }
329
+ .stack-natural :deep(.overflow-hidden),
330
+ .stack-natural :deep(.overflow-auto),
331
+ .stack-natural :deep(.overflow-y-auto),
332
+ .stack-natural :deep(.overflow-x-auto) {
333
+ overflow: visible !important;
334
+ }
335
+ .stack-natural :deep(.flex-1) {
336
+ flex: 0 0 auto !important;
337
+ }
338
+
339
+ /* Collapse the nested chrome that text-response draws around its
340
+ Markdown output so it reads like plain content inside our stack card
341
+ instead of creating a second border/shadow "card" inside ours. */
342
+ .stack-text-response :deep(.text-response-content-wrapper > .p-6) {
343
+ padding: 0.5rem 0.75rem;
344
+ }
345
+ .stack-text-response :deep(.text-response-container .max-w-3xl) {
346
+ max-width: none;
347
+ margin-left: 0;
348
+ margin-right: 0;
349
+ }
350
+ .stack-text-response :deep(.text-response-container .mb-2) {
351
+ display: none; /* redundant role header — stack card header shows it already */
352
+ }
353
+ .stack-text-response :deep(.text-response-container .shadow-sm) {
354
+ border: 0;
355
+ box-shadow: none;
356
+ padding: 0;
357
+ background: transparent;
358
+ border-radius: 0;
359
+ }
360
+ </style>
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <div v-if="queries.length > 0" class="border-t border-gray-200">
3
+ <div v-if="expanded" ref="listRef" class="px-4 pt-2 max-h-64 overflow-y-auto flex flex-col gap-1">
4
+ <button
5
+ v-for="query in queries"
6
+ :key="query"
7
+ class="text-left text-xs bg-gray-100 hover:bg-gray-200 text-gray-700 rounded px-3 py-1.5 border border-gray-300 transition-colors"
8
+ @click="onClick($event, query)"
9
+ >
10
+ {{ query }}
11
+ </button>
12
+ <p class="text-center text-[10px] text-gray-400 py-0.5">click to send · shift+click to edit</p>
13
+ </div>
14
+ <button
15
+ class="w-full flex items-center justify-between px-4 py-1 text-xs text-gray-500 hover:text-gray-700 hover:bg-gray-50 transition-colors"
16
+ @click="expanded = !expanded"
17
+ >
18
+ <span class="flex items-center gap-1">
19
+ <span class="material-icons text-sm">lightbulb</span>
20
+ Suggestions
21
+ </span>
22
+ <span class="material-icons text-sm transition-transform" :class="{ 'rotate-180': !expanded }">expand_less</span>
23
+ </button>
24
+ </div>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { nextTick, ref, watch } from "vue";
29
+
30
+ defineProps<{
31
+ queries: string[];
32
+ }>();
33
+
34
+ const emit = defineEmits<{
35
+ send: [query: string];
36
+ edit: [query: string];
37
+ }>();
38
+
39
+ const expanded = ref(false);
40
+ const listRef = ref<HTMLDivElement | null>(null);
41
+
42
+ watch(expanded, (isExpanded) => {
43
+ if (!isExpanded) return;
44
+ nextTick(() => {
45
+ if (listRef.value) {
46
+ listRef.value.scrollTop = listRef.value.scrollHeight;
47
+ }
48
+ });
49
+ });
50
+
51
+ function onClick(e: MouseEvent, query: string): void {
52
+ expanded.value = false;
53
+ if (e.shiftKey) {
54
+ emit("edit", query);
55
+ return;
56
+ }
57
+ emit("send", query);
58
+ }
59
+
60
+ function collapse(): void {
61
+ expanded.value = false;
62
+ }
63
+
64
+ defineExpose({ collapse });
65
+ </script>