shadow-claw 1.23.3 → 1.24.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 (286) hide show
  1. package/README.md +42 -5
  2. package/bin/assert-version-bump.mjs +60 -38
  3. package/bin/assert-version-bump.test.mjs +74 -11
  4. package/bin/cli.mjs +166 -0
  5. package/bin/cli.test.mjs +60 -0
  6. package/bin/commands/backup.mjs +70 -0
  7. package/bin/commands/clients.mjs +41 -0
  8. package/bin/commands/peer-id.mjs +76 -0
  9. package/bin/commands/peer-id.test.mjs +104 -0
  10. package/bin/commands/send.mjs +51 -0
  11. package/bin/commands/tasks.mjs +59 -0
  12. package/bin/commands/webrtc-listen.mjs +254 -0
  13. package/bin/prerender-dsd-shell/prerender-dsd-shell.test.mjs +46 -0
  14. package/bin/rolldown.config.test.mjs +401 -8
  15. package/bin/utils/control-client.mjs +243 -0
  16. package/bin/utils/control-client.test.mjs +83 -0
  17. package/bin/utils/ulid.mjs +45 -0
  18. package/bin/utils/webrtc-control-client.mjs +699 -0
  19. package/bin/utils/webrtc-control-client.test.mjs +318 -0
  20. package/dist/electron/main.cjs +1 -1
  21. package/dist/public/AGENTS.md +5 -3
  22. package/dist/public/README.md +42 -5
  23. package/dist/public/agent.worker.js +6 -6
  24. package/dist/public/{app-routes-B0ePT49v.js → app-routes-Bs-riIvr.js} +1 -1
  25. package/dist/public/backup-controller-C_s9in2t.js +1 -0
  26. package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +1 -0
  27. package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +1 -0
  28. package/dist/public/components/settings/shadow-claw-storage/shadow-claw-storage.html +1 -1
  29. package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +1 -1
  30. package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
  31. package/dist/public/components/shadow-claw-files/shadow-claw-files.css +1 -1
  32. package/dist/public/components/shadow-claw-files/shadow-claw-files.html +1 -1
  33. package/dist/public/components/shadow-claw-settings/shadow-claw-settings.html +1 -1
  34. package/dist/public/config-CrHchneq.js +1 -0
  35. package/dist/public/{configurePeerJs-B7kNU8AQ.js → configurePeerJs-CxpZeDJJ.js} +1 -1
  36. package/dist/public/{connections-xd_66YVU.js → connections-DvbgdkaB.js} +1 -1
  37. package/dist/public/{custom-element-security-CZ92anB8.js → custom-element-security-NCqQ5lmC.js} +1 -1
  38. package/dist/public/docs/README.md +3 -1
  39. package/dist/public/docs/architecture/overview.md +12 -12
  40. package/dist/public/docs/architecture/storage.md +5 -2
  41. package/dist/public/docs/decisions/bundled-typescript-architecture.md +1 -1
  42. package/dist/public/docs/decisions/indexeddb-and-opfs-storage.md +6 -3
  43. package/dist/public/docs/decisions/native-web-components-and-signals.md +1 -1
  44. package/dist/public/docs/example/article/index.html +2 -2
  45. package/dist/public/docs/guides/configuring-messaging-channels.md +41 -6
  46. package/dist/public/docs/guides/server-development-configuration.md +24 -0
  47. package/dist/public/docs/publishing/index.html +19 -76
  48. package/dist/public/docs/skill-creator/index.html +2 -2
  49. package/dist/public/docs/subsystems/backup.md +56 -0
  50. package/dist/public/docs/subsystems/cli.md +124 -0
  51. package/dist/public/docs/subsystems/control-plane.md +158 -0
  52. package/dist/public/docs/subsystems/notifications.md +54 -2
  53. package/dist/public/docs/subsystems/reactive-ui.md +1 -0
  54. package/dist/public/downloadGroupFile-By0GR6eV.js +1 -0
  55. package/dist/public/e2e/README.md +5 -1
  56. package/dist/public/{e2e-bridge-DkS5xFGX.js → e2e-bridge-IGWsRl4i.js} +1 -1
  57. package/dist/public/file-viewer-D0eflgt6.js +1 -0
  58. package/dist/public/files/main/~/docs/publishing-to-github-pages.md +17 -82
  59. package/dist/public/getAllTasks-vpBlgGsc.js +1 -0
  60. package/dist/public/getGroupDir-P1h9wl6S.js +1 -0
  61. package/dist/public/{git-BkjrsRXX.js → git-7Vq_SJSp.js} +2 -2
  62. package/dist/public/{iframe-sanitizer-L6Jyu__Z.js → iframe-sanitizer-y2Z1pPwY.js} +1 -1
  63. package/dist/public/index.html +2 -2
  64. package/dist/public/index.js +1 -1
  65. package/dist/public/{initChatSplitResize-BslPXAXS.js → initChatSplitResize-CzV2E7Ul.js} +1 -1
  66. package/dist/public/initControlPlane-C9jpnWev.js +1 -0
  67. package/dist/public/llms.txt +1 -1
  68. package/dist/public/main/index.html +2 -2
  69. package/dist/public/main/memory/index.html +2 -2
  70. package/dist/public/{markdown-6MBrIo-S.js → markdown-BnpKxSxl.js} +1 -1
  71. package/dist/public/mcp-reconnect-9emsqGRe.js +1 -0
  72. package/dist/public/{memoryStorage-CjGseKI1.js → memoryStorage-C0KvLNUp.js} +1 -1
  73. package/dist/public/{orchestrator-B9KmIj8Q.js → orchestrator-3h9xxHUm.js} +676 -676
  74. package/dist/public/orchestrator-nJj59RqD.js +15 -0
  75. package/dist/public/pages/main/~/docs/publishing-to-github-pages.md +17 -82
  76. package/dist/public/{peerjs-Dv8JNm7v.js → peerjs-BP4laMXj.js} +1 -1
  77. package/dist/public/{push-client-Dqq06wbB.js → push-client-CrjEXK3U.js} +1 -1
  78. package/dist/public/service-worker/fetch-proxy.js +1 -1
  79. package/dist/public/service-worker/push-handler.js +1 -1
  80. package/dist/public/service-worker.js +1 -1
  81. package/dist/public/{shadow-claw-Bd1rfuVC.js → shadow-claw-CbwETBWf.js} +2 -2
  82. package/dist/public/{shadow-claw-accounts-DL2l3oM0.js → shadow-claw-accounts-B4sBTQEj.js} +3 -3
  83. package/dist/public/{shadow-claw-channels-BGXDsQ38.js → shadow-claw-channels-BDWG5Qa3.js} +1 -1
  84. package/dist/public/{shadow-claw-chat-NXg6zz_l.js → shadow-claw-chat-Crv0DTCY.js} +6 -6
  85. package/dist/public/shadow-claw-control-plane-CX1IGbh9.js +199 -0
  86. package/dist/public/{shadow-claw-conversations-C8eqGpP9.js → shadow-claw-conversations-_9qAaRXk.js} +1 -1
  87. package/dist/public/shadow-claw-dialog-BrOwAOdk.js +1 -0
  88. package/dist/public/shadow-claw-file-viewer-Blf6yGVD.js +577 -0
  89. package/dist/public/shadow-claw-files-CYySBDCl.js +840 -0
  90. package/dist/public/{shadow-claw-git-C0ntz7aX.js → shadow-claw-git-v8S0JjuR.js} +3 -3
  91. package/dist/public/{shadow-claw-integrations-DCXV2r69.js → shadow-claw-integrations-D7ufpwhX.js} +1 -1
  92. package/dist/public/{shadow-claw-llm-CudgFQsS.js → shadow-claw-llm-GWgoVfhI.js} +2 -2
  93. package/dist/public/{shadow-claw-mcp-remote-B8RkpX28.js → shadow-claw-mcp-remote-kY4LMFNN.js} +1 -1
  94. package/dist/public/{shadow-claw-networking-BLiRsrsK.js → shadow-claw-networking-qHbP95z9.js} +1 -1
  95. package/dist/public/{shadow-claw-notifications-CQhQcjtL.js → shadow-claw-notifications-YyHOK2Fr.js} +1 -1
  96. package/dist/public/{shadow-claw-page-header-NeRD0tKU.js → shadow-claw-page-header-DMKbkv3s.js} +1 -1
  97. package/dist/public/{shadow-claw-pages-DCHnFh6r.js → shadow-claw-pages-DFZHhzFZ.js} +3 -3
  98. package/dist/public/{shadow-claw-settings-VAfjKFQg.js → shadow-claw-settings-DhxrKIfA.js} +10 -3
  99. package/dist/public/shadow-claw-storage-BFynbP0j.js +219 -0
  100. package/dist/public/{shadow-claw-task-server-Dk33N_1G.js → shadow-claw-task-server-3BIeb5oZ.js} +25 -4
  101. package/dist/public/{shadow-claw-tasks-xA1VfmWN.js → shadow-claw-tasks-Caxc9atL.js} +4 -4
  102. package/dist/public/{shadow-claw-terminal-Du2r0ciq.js → shadow-claw-terminal-Beb5qF-U.js} +1 -1
  103. package/dist/public/{shadow-claw-tools-CZj4rq7l.js → shadow-claw-tools-BfJx3A9W.js} +2 -2
  104. package/dist/public/{shadow-claw-webvm-JYUkWICG.js → shadow-claw-webvm-BCS8ykeP.js} +1 -1
  105. package/dist/public/static-main/~/docs/publishing-to-github-pages.md +17 -82
  106. package/dist/public/static-main-manifest.json +1 -1
  107. package/dist/public/{syncWebMcpRegistration-DfpvASnh.js → syncWebMcpRegistration-B_M2fCjH.js} +1 -1
  108. package/dist/server.js +286 -189
  109. package/package.json +2 -1
  110. package/src/__mocks__/jszip.cjs +15 -5
  111. package/src/components/settings/shadow-claw-accounts/shadow-claw-accounts.test.ts +35 -0
  112. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +117 -0
  113. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +81 -0
  114. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.test.ts +127 -0
  115. package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.ts +292 -0
  116. package/src/components/settings/shadow-claw-git/shadow-claw-git.test.ts +72 -0
  117. package/src/components/settings/shadow-claw-integrations/connection-test-auth.test.ts +20 -30
  118. package/src/components/settings/shadow-claw-integrations/shadow-claw-integrations.test.ts +116 -0
  119. package/src/components/settings/shadow-claw-llm/model-ranking.test.ts +143 -51
  120. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.test.ts +307 -4
  121. package/src/components/settings/shadow-claw-llm/shadow-claw-llm.ts +8 -8
  122. package/src/components/settings/shadow-claw-mcp-remote/shadow-claw-mcp-remote.test.ts +31 -0
  123. package/src/components/settings/shadow-claw-peerjs/shadow-claw-peerjs.test.ts +186 -0
  124. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.html +17 -0
  125. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.test.ts +120 -0
  126. package/src/components/settings/shadow-claw-storage/shadow-claw-storage.ts +56 -0
  127. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +23 -2
  128. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
  129. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.test.ts +19 -0
  130. package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.ts +2 -1
  131. package/src/components/shadow-claw/utils/initChatSplitResize.test.ts +180 -13
  132. package/src/components/shadow-claw/utils/setupEffects.test.ts +39 -0
  133. package/src/components/shadow-claw-a2ui/catalog/basic/shared.test.ts +117 -0
  134. package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.test.ts +1159 -1
  135. package/src/components/shadow-claw-chat/shadow-claw-chat.test.ts +1523 -19
  136. package/src/components/shadow-claw-chat/utils/escapeHtml.test.ts +13 -0
  137. package/src/components/shadow-claw-chat/utils/getPeerChatDisplayStatus.test.ts +20 -0
  138. package/src/components/shadow-claw-conversations/shadow-claw-conversations.test.ts +494 -0
  139. package/src/components/shadow-claw-dialog/shadow-claw-dialog.ts +13 -2
  140. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +378 -0
  141. package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +11 -9
  142. package/src/components/shadow-claw-files/shadow-claw-files.css +41 -17
  143. package/src/components/shadow-claw-files/shadow-claw-files.html +35 -0
  144. package/src/components/shadow-claw-files/shadow-claw-files.test.ts +627 -1
  145. package/src/components/shadow-claw-files/shadow-claw-files.ts +170 -6
  146. package/src/components/shadow-claw-pages/utils/resolveWorkspaceLinkPath.test.ts +31 -0
  147. package/src/components/shadow-claw-pages/utils/resolveWorkspaceLinkPath.ts +2 -1
  148. package/src/components/shadow-claw-pages/utils/rewriteWorkspacePreviewHtml.test.ts +15 -0
  149. package/src/components/shadow-claw-pdf-viewer/utils/installGetOrInsertComputedPolyfill.test.ts +65 -0
  150. package/src/components/shadow-claw-settings/shadow-claw-settings.html +7 -0
  151. package/src/components/shadow-claw-settings/shadow-claw-settings.test.ts +221 -0
  152. package/src/components/shadow-claw-settings/shadow-claw-settings.ts +2 -0
  153. package/src/components/shadow-claw-tasks/shadow-claw-tasks.test.ts +407 -0
  154. package/src/components/shadow-claw-terminal/utils/consumeEscapeSequence.test.ts +58 -0
  155. package/src/components/shadow-claw-tools/shadow-claw-tools.test.ts +80 -0
  156. package/src/config/config.test.ts +9 -0
  157. package/src/config/config.ts +15 -0
  158. package/src/core/app-routes.test.ts +56 -0
  159. package/src/core/app-routes.ts +8 -1
  160. package/src/core/backup-controller.test.ts +101 -0
  161. package/src/core/backup-controller.ts +137 -0
  162. package/src/core/control-plane-client.test.ts +408 -0
  163. package/src/core/control-plane-client.ts +425 -0
  164. package/src/core/orchestrator/orchestrator.ts +4 -0
  165. package/src/core/orchestrator/utils/compactContext.test.ts +78 -0
  166. package/src/core/orchestrator/utils/operations/channel.test.ts +254 -9
  167. package/src/core/orchestrator/utils/operations/provider.test.ts +326 -34
  168. package/src/core/orchestrator/utils/operations/vm.test.ts +114 -2
  169. package/src/core/orchestrator/utils/syncProxyConfigToServiceWorker.test.ts +60 -0
  170. package/src/core/utils/initControlPlane.test.ts +276 -0
  171. package/src/core/utils/initControlPlane.ts +437 -0
  172. package/src/core/utils/initializeApp.ts +18 -0
  173. package/src/db/getSession.test.ts +13 -1
  174. package/src/db/openDatabase.test.ts +10 -1
  175. package/src/db/openDatabase.ts +3 -1
  176. package/src/server/app.test.ts +12 -0
  177. package/src/server/app.ts +18 -8
  178. package/src/server/client-registry.test.ts +250 -0
  179. package/src/server/client-registry.ts +477 -0
  180. package/src/server/config.ts +15 -0
  181. package/src/server/control-plane-types.ts +120 -0
  182. package/src/server/control-plane.test.ts +472 -0
  183. package/src/server/control-plane.ts +581 -0
  184. package/src/server/routes/backup.test.ts +263 -0
  185. package/src/server/routes/backup.ts +262 -0
  186. package/src/server/routes/integrations-email.test.ts +532 -0
  187. package/src/server/routes/llamafile.test.ts +172 -48
  188. package/src/server/routes/mesh-llm.test.ts +192 -0
  189. package/src/server/routes/ollama.test.ts +49 -0
  190. package/src/server/server-peer.test.ts +86 -0
  191. package/src/server/server-peer.ts +330 -0
  192. package/src/server/server.ts +35 -0
  193. package/src/server/services/llamafile-manager.test.ts +230 -6
  194. package/src/server/services/transformers-runtime.test.ts +186 -18
  195. package/src/server/webrtc.test.ts +21 -0
  196. package/src/server/webrtc.ts +72 -0
  197. package/src/service-worker/fetch-proxy.test.ts +64 -1
  198. package/src/service-worker/push-handler.ts +39 -0
  199. package/src/shell/vm.test.ts +744 -12
  200. package/src/storage/ensureMainGroupIndex.test.ts +153 -0
  201. package/src/storage/getGroupFile.test.ts +66 -0
  202. package/src/storage/memoryStorage.test.ts +82 -2
  203. package/src/storage/migrateLegacyOpfs.test.ts +327 -0
  204. package/src/storage/migrateLegacyOpfs.ts +117 -0
  205. package/src/storage/writeGroupFileBytes.test.ts +143 -0
  206. package/src/stores/chat-ui.test.ts +20 -0
  207. package/src/stores/orchestrator.test.ts +134 -0
  208. package/src/stores/toast.test.ts +50 -0
  209. package/src/stores/tools.test.ts +147 -10
  210. package/src/subsystems/channels/imessage.test.ts +59 -0
  211. package/src/subsystems/channels/peerjs.test.ts +500 -62
  212. package/src/subsystems/channels/peerjs.ts +71 -4
  213. package/src/subsystems/channels/telegram.test.ts +177 -0
  214. package/src/subsystems/email/catalog.test.ts +1 -0
  215. package/src/subsystems/email/connections.test.ts +39 -1
  216. package/src/subsystems/git/credentials.test.ts +160 -825
  217. package/src/subsystems/git/git.test.ts +463 -0
  218. package/src/subsystems/notifications/__mocks__/node-sqlite.cjs +12 -1
  219. package/src/subsystems/notifications/push-routes.test.ts +33 -0
  220. package/src/subsystems/notifications/push-routes.ts +23 -0
  221. package/src/subsystems/notifications/push-store.ts +1 -1
  222. package/src/subsystems/providers/builtin-ai-tasks.test.ts +74 -0
  223. package/src/subsystems/providers/litert-lm-provider.test.ts +212 -51
  224. package/src/subsystems/providers/model-registry.test.ts +115 -0
  225. package/src/subsystems/providers/providers.test.ts +94 -1
  226. package/src/subsystems/providers/transformers-js-provider.test.ts +279 -0
  227. package/src/subsystems/providers/utils/createModelCacheFetch.test.ts +2 -2
  228. package/src/subsystems/providers/utils/downloadModelToCache.test.ts +2 -2
  229. package/src/subsystems/providers/utils/loadModelStream.test.ts +2 -2
  230. package/src/subsystems/skills/activateSkill.test.ts +101 -0
  231. package/src/subsystems/skills/discoverSkills.test.ts +251 -0
  232. package/src/subsystems/tools/cron.test.ts +64 -0
  233. package/src/subsystems/tools/declarative.test.ts +255 -89
  234. package/src/testing/assets/v86.9pfs/libv86.mjs +63 -0
  235. package/src/testing/assets/v86.ext2/libv86.mjs +3 -0
  236. package/src/testing/assets_missing_v86/assets/v86.9pfs/libv86.mjs +1 -0
  237. package/src/testing/jest-setup.ts +328 -40
  238. package/src/testing/mock-v86.mjs +49 -0
  239. package/src/ui/a2ui/types.ts +4 -3
  240. package/src/ui/a2ui/utils/normaliseComponentsToMap.test.ts +102 -0
  241. package/src/ui/a2ui/utils/resolveChildIds.test.ts +63 -0
  242. package/src/ui/a2ui/utils/resolveDynamicNumber.test.ts +13 -1
  243. package/src/worker/tools/bash/utils/waitForVMReady.test.ts +40 -0
  244. package/src/worker/tools/email/email.test.ts +193 -8
  245. package/src/worker/tools/email/utils/asEmailAttachmentInputs.test.ts +47 -0
  246. package/src/worker/tools/git/git.test.ts +214 -3
  247. package/src/worker/tools/rooms/invite-to-room.test.ts +54 -0
  248. package/src/worker/tools/rooms/leave-room.test.ts +40 -0
  249. package/src/worker/tools/rooms/list-room-members.test.ts +75 -0
  250. package/src/worker/tools/tasks/tasks-utils.test.ts +22 -0
  251. package/src/worker/tools/ui/ask-user.test.ts +50 -0
  252. package/src/worker/tools/ui/clear-chat.test.ts +19 -0
  253. package/src/worker/tools/ui/get-current-time.test.ts +19 -0
  254. package/src/worker/tools/ui/javascript.test.ts +116 -0
  255. package/src/worker/tools/ui/list-components.test.ts +10 -0
  256. package/src/worker/tools/ui/list-tool-profiles.test.ts +42 -0
  257. package/src/worker/tools/ui/manage-tools.test.ts +34 -0
  258. package/src/worker/tools/ui/render-component.test.ts +346 -0
  259. package/src/worker/tools/ui/send-notification.test.ts +40 -0
  260. package/src/worker/tools/ui/utils/getAllowFullInternetAccess.test.ts +31 -0
  261. package/src/worker/tools/ui/utils/parseBooleanConfig.test.ts +25 -0
  262. package/src/worker/tools/workspace/attach-file.test.ts +84 -0
  263. package/src/worker/tools/workspace/copy-file.test.ts +69 -0
  264. package/src/worker/tools/workspace/create-directory.test.ts +40 -0
  265. package/src/worker/tools/workspace/delete-file.test.ts +73 -0
  266. package/src/worker/tools/workspace/diff-files.test.ts +94 -0
  267. package/src/worker/tools/workspace/list-files.test.ts +36 -0
  268. package/src/worker/tools/workspace/move-file.test.ts +66 -0
  269. package/src/worker/tools/workspace/open-file.test.ts +57 -0
  270. package/src/worker/tools/workspace/patch-file.test.ts +81 -0
  271. package/src/worker/tools/workspace/read-file.test.ts +116 -0
  272. package/src/worker/tools/workspace/send-file.test.ts +82 -0
  273. package/src/worker/tools/workspace/update-memory.test.ts +29 -0
  274. package/src/worker/tools/workspace/write-file.test.ts +28 -0
  275. package/src/worker/utils/rate-limit.test.ts +199 -0
  276. package/src/worker/utils/tool-state.test.ts +29 -0
  277. package/src/worker/utils/transformers-js-utils.test.ts +59 -0
  278. package/dist/public/config-AL77QAx4.js +0 -1
  279. package/dist/public/downloadGroupFile-BABv_1mJ.js +0 -1
  280. package/dist/public/file-viewer-CCSABMsE.js +0 -1
  281. package/dist/public/mcp-reconnect-DoicazQd.js +0 -1
  282. package/dist/public/orchestrator-4su7iyWj.js +0 -15
  283. package/dist/public/shadow-claw-dialog-ByyHgxM9.js +0 -1
  284. package/dist/public/shadow-claw-file-viewer-9Qkwc30T.js +0 -577
  285. package/dist/public/shadow-claw-files-aDyq6Hv7.js +0 -781
  286. package/dist/public/shadow-claw-storage-Cf31np81.js +0 -202
@@ -45,6 +45,8 @@ jest.unstable_mockModule("../../stores/orchestrator.js", () => ({
45
45
  toggleTask: jest.fn(),
46
46
  getTasksByGroupId: jest.fn(() => []),
47
47
  getTaskServerTerminalInstance: jest.fn(() => null),
48
+ reorderTasks: jest.fn(),
49
+ restoreTasksFromBackup: jest.fn(),
48
50
  },
49
51
  }));
50
52
 
@@ -261,5 +263,410 @@ describe("shadow-claw-tasks", () => {
261
263
  }),
262
264
  );
263
265
  });
266
+
267
+ it("manages tools editor UI interactions (remove, modify, suppress)", () => {
268
+ const el = new ShadowClawTasks();
269
+ document.body.appendChild(el);
270
+
271
+ el.editingTools = [
272
+ {
273
+ name: "test_tool",
274
+ input: { a: 1 },
275
+ suppressOutput: false,
276
+ suppressToast: false,
277
+ },
278
+ ];
279
+
280
+ el.renderToolsEditor();
281
+ const toolsList = el.shadowRoot?.querySelector(
282
+ ".tasks__tools-list",
283
+ ) as HTMLElement;
284
+ expect(toolsList.querySelectorAll(".tasks__tool-item").length).toBe(1);
285
+
286
+ // Modify tool name
287
+ const nameInput = toolsList.querySelector(
288
+ ".tasks__form-input",
289
+ ) as HTMLInputElement;
290
+ nameInput.value = "updated_tool";
291
+ nameInput.dispatchEvent(new Event("input"));
292
+ expect(el.editingTools[0].name).toBe("updated_tool");
293
+
294
+ // Modify textarea params
295
+ const textarea = toolsList.querySelector(
296
+ ".tasks__form-textarea",
297
+ ) as HTMLTextAreaElement;
298
+ textarea.value = '{"foo": "bar"}';
299
+ textarea.dispatchEvent(new Event("change"));
300
+ expect(el.editingTools[0].input).toEqual({ foo: "bar" });
301
+
302
+ // Toggle suppress
303
+ const suppressCheckbox = toolsList.querySelector(
304
+ ".tasks__tool-suppress input",
305
+ ) as HTMLInputElement;
306
+ suppressCheckbox.checked = true;
307
+ suppressCheckbox.dispatchEvent(new Event("change"));
308
+ expect(el.editingTools[0].suppressOutput).toBe(true);
309
+
310
+ // Remove tool
311
+ const removeBtn = toolsList.querySelector(
312
+ ".tasks__tool-remove-btn",
313
+ ) as HTMLButtonElement;
314
+ removeBtn.click();
315
+ expect(el.editingTools.length).toBe(0);
316
+
317
+ document.body.removeChild(el);
318
+ });
319
+
320
+ it("renders tasks and binds action buttons (toggle, copy, run, edit, delete)", async () => {
321
+ const el = new ShadowClawTasks();
322
+ const container = document.createElement("div");
323
+ container.className = "tasks__list";
324
+ const countEl = document.createElement("span");
325
+ countEl.className = "tasks__count";
326
+ el.shadowRoot?.replaceChildren(countEl, container);
327
+
328
+ const db = {} as any;
329
+ const tasks = [
330
+ {
331
+ id: "task-1",
332
+ name: "Daily Job",
333
+ schedule: "0 0 * * *",
334
+ prompt: "check status",
335
+ enabled: true,
336
+ type: "prompt",
337
+ freshContext: true,
338
+ },
339
+ ];
340
+ (orchestratorStore as any).tasks = tasks;
341
+
342
+ await el.updateTaskList(db);
343
+ expect(container.querySelectorAll(".tasks__item").length).toBe(1);
344
+
345
+ const item = container.querySelector(".tasks__item") as HTMLElement;
346
+ const toggleInput = item.querySelector(
347
+ ".tasks__toggle-input",
348
+ ) as HTMLInputElement;
349
+ toggleInput.checked = false;
350
+ toggleInput.dispatchEvent(new Event("change"));
351
+ expect(orchestratorStore.toggleTask).toHaveBeenCalledWith(
352
+ db,
353
+ tasks[0],
354
+ false,
355
+ );
356
+
357
+ const runBtn = item.querySelector(".tasks__run-btn") as HTMLButtonElement;
358
+ runBtn.click();
359
+ expect(orchestratorStore.runTask).toHaveBeenCalledWith(tasks[0], true);
360
+ });
361
+
362
+ it("handles restore with valid and invalid files", async () => {
363
+ const el = new ShadowClawTasks();
364
+ const db = {} as any;
365
+
366
+ // 1. No file selected
367
+ const emptyInput = document.createElement("input");
368
+ emptyInput.type = "file";
369
+ await el.handleRestore(db, emptyInput);
370
+
371
+ // 2. Valid JSON file
372
+ const backupData = [
373
+ {
374
+ id: "restored-1",
375
+ schedule: "0 0 * * *",
376
+ prompt: "daily report",
377
+ enabled: true,
378
+ },
379
+ ];
380
+ const validFile = new File([JSON.stringify(backupData)], "backup.json", {
381
+ type: "application/json",
382
+ });
383
+ const fileInput = document.createElement("input");
384
+ fileInput.type = "file";
385
+ Object.defineProperty(fileInput, "files", { value: [validFile] });
386
+
387
+ await el.handleRestore(db, fileInput);
388
+ expect(orchestratorStore.upsertTask).toHaveBeenCalled();
389
+ });
390
+
391
+ it("handles preview link clicks and opens in file viewer", async () => {
392
+ const el = new ShadowClawTasks();
393
+ const db = {} as any;
394
+
395
+ const link = document.createElement("a");
396
+ link.setAttribute("href", "docs/spec.md");
397
+ const targetDiv = document.createElement("div");
398
+ targetDiv.appendChild(link);
399
+
400
+ const event = new MouseEvent("click", {
401
+ bubbles: true,
402
+ cancelable: true,
403
+ });
404
+ Object.defineProperty(event, "target", { value: link });
405
+ Object.defineProperty(event, "button", { value: 0 });
406
+
407
+ const fileViewerMod = await import("../../stores/file-viewer.js");
408
+ await el.handlePreviewLinkClick(event, db);
409
+ expect(fileViewerMod.fileViewerStore.openFile).toHaveBeenCalledWith(
410
+ db,
411
+ "docs/spec.md",
412
+ "default",
413
+ );
414
+ });
415
+
416
+ it("cleans up on disconnectedCallback", () => {
417
+ const el = new ShadowClawTasks();
418
+ const cleanupMock = jest.fn();
419
+ el.cleanup = cleanupMock;
420
+ el.disconnectedCallback();
421
+ expect(cleanupMock).toHaveBeenCalledTimes(1);
422
+ });
423
+
424
+ it("renders tools preview for tool task chains", () => {
425
+ const el = new ShadowClawTasks();
426
+ const tools = [
427
+ { name: "read_file", input: { path: "hello.txt" } },
428
+ { name: "write_file", input: { path: "out.txt", content: "$pipe" } },
429
+ ];
430
+ const previewHtml = el.renderToolsPreview(tools);
431
+ expect(previewHtml).toContain("read_file");
432
+ expect(previewHtml).toContain("write_file");
433
+ expect(previewHtml).toContain("hello.txt");
434
+ });
435
+
436
+ it("handles clear all tasks when confirmed", async () => {
437
+ const el = new ShadowClawTasks();
438
+ const db = {} as any;
439
+ window.confirm = jest.fn(() => true);
440
+
441
+ await el.handleClearAll(db);
442
+ expect(orchestratorStore.clearAllTasks).toHaveBeenCalledWith(db);
443
+
444
+ window.confirm = jest.fn(() => false);
445
+ (orchestratorStore.clearAllTasks as any).mockClear();
446
+ await el.handleClearAll(db);
447
+ expect(orchestratorStore.clearAllTasks).not.toHaveBeenCalled();
448
+ });
449
+
450
+ it("handles edit submission for prompt and tool tasks", async () => {
451
+ const el = new ShadowClawTasks();
452
+ const db = {} as any;
453
+
454
+ const form = document.createElement("form");
455
+ const scheduleInput = document.createElement("input");
456
+ scheduleInput.name = "schedule";
457
+ scheduleInput.value = "*/15 * * * *";
458
+
459
+ const promptInput = document.createElement("textarea");
460
+ promptInput.name = "prompt";
461
+ promptInput.value = "Run automated scan";
462
+
463
+ const freshContextInput = document.createElement("input");
464
+ freshContextInput.name = "freshContext";
465
+ freshContextInput.type = "checkbox";
466
+ freshContextInput.checked = true;
467
+
468
+ const subagentInput = document.createElement("input");
469
+ subagentInput.name = "subagent";
470
+ subagentInput.type = "checkbox";
471
+ subagentInput.checked = false;
472
+
473
+ const promptRadio = document.createElement("input");
474
+ promptRadio.name = "taskType";
475
+ promptRadio.value = "prompt";
476
+ promptRadio.checked = true;
477
+
478
+ form.append(
479
+ scheduleInput,
480
+ promptInput,
481
+ freshContextInput,
482
+ subagentInput,
483
+ promptRadio,
484
+ );
485
+
486
+ const dialog = document.createElement("dialog");
487
+ dialog.close = jest.fn();
488
+ el.shadowRoot?.appendChild(dialog);
489
+
490
+ await el.handleEditSubmit(db, form);
491
+ expect(orchestratorStore.upsertTask).toHaveBeenCalledWith(
492
+ db,
493
+ expect.objectContaining({
494
+ schedule: "*/15 * * * *",
495
+ prompt: "Run automated scan",
496
+ freshContext: true,
497
+ type: "prompt",
498
+ }),
499
+ );
500
+ });
501
+
502
+ it("handles copyId, delete, reorder, restore, and renderPreview", async () => {
503
+ const el = new ShadowClawTasks() as any;
504
+ const db = {} as any;
505
+
506
+ // 1. Copy ID
507
+ const mockClipboard = {
508
+ writeText: jest.fn<any>().mockResolvedValue(undefined),
509
+ };
510
+ Object.assign(navigator, { clipboard: mockClipboard });
511
+ await el.handleCopyId("task-123");
512
+ expect(mockClipboard.writeText).toHaveBeenCalledWith("task-123");
513
+
514
+ // 2. Delete task with confirmation
515
+ el.requestConfirmation = jest.fn<any>().mockResolvedValue(true);
516
+ await el.handleDelete(db, "task-123");
517
+ expect(orchestratorStore.deleteTask).toHaveBeenCalledWith(db, "task-123");
518
+
519
+ // 3. Reorder tasks
520
+ await el.handleReorder("t1", "t2", ["t2", "t1"]);
521
+ expect(orchestratorStore.reorderTasks).toHaveBeenCalledWith(
522
+ expect.anything(),
523
+ "default",
524
+ ["t2", "t1"],
525
+ );
526
+
527
+ // 4. Restore tasks from JSON
528
+ const restoreFile = {
529
+ name: "tasks-backup.json",
530
+ text: jest
531
+ .fn<() => Promise<string>>()
532
+ .mockResolvedValue(JSON.stringify([{ id: "t1" }])),
533
+ };
534
+ const restoreInput = {
535
+ files: [restoreFile],
536
+ value: "tasks-backup.json",
537
+ } as any;
538
+ await el.handleRestore(db, restoreInput);
539
+ expect(orchestratorStore.restoreTasksFromBackup).toHaveBeenCalledWith(
540
+ db,
541
+ [{ id: "t1" }],
542
+ );
543
+
544
+ // 5. Render preview
545
+ const emptyPreview = await el.renderPreview("");
546
+ expect(emptyPreview).toContain("No content");
547
+
548
+ const shortPreview = await el.renderPreview("Short prompt");
549
+ expect(shortPreview).toContain("tasks__prompt");
550
+
551
+ const longPreview = await el.renderPreview(
552
+ "Long\nmulti-line\nprompt",
553
+ true,
554
+ );
555
+ expect(longPreview).toContain("tasks__content-details");
556
+ });
557
+
558
+ it("handles keyboard navigation, announcement, and autoscroll", async () => {
559
+ const el = new ShadowClawTasks() as any;
560
+ document.body.appendChild(el);
561
+
562
+ // 1. _announce
563
+ const liveRegion = document.createElement("div");
564
+ liveRegion.id = "live-region";
565
+ el.shadowRoot?.appendChild(liveRegion);
566
+ el._announce("Task test announcement");
567
+
568
+ // 2. Keyboard grab and navigation
569
+ (orchestratorStore as any).tasks = [
570
+ { id: "t1", name: "Task 1", prompt: "Hello 1" },
571
+ { id: "t2", name: "Task 2", prompt: "Hello 2" },
572
+ ];
573
+
574
+ // Grab with 'm'
575
+ const keyM = new KeyboardEvent("keydown", { key: "m", cancelable: true });
576
+ el._handleKeyboard(keyM, "t1", "Task 1");
577
+ expect(el._keyboardGrabbedId).toBe("t1");
578
+
579
+ // Move down with ArrowDown
580
+ const keyDown = new KeyboardEvent("keydown", {
581
+ key: "ArrowDown",
582
+ cancelable: true,
583
+ });
584
+ el._handleKeyboard(keyDown, "t1", "Task 1");
585
+
586
+ // Drop with Space
587
+ const keySpace = new KeyboardEvent("keydown", {
588
+ key: " ",
589
+ cancelable: true,
590
+ });
591
+ el._handleKeyboard(keySpace, "t1", "Task 1");
592
+ expect(el._keyboardGrabbedId).toBeNull();
593
+
594
+ // Cancel with Escape
595
+ el._keyboardGrabbedId = "t1";
596
+ const keyEsc = new KeyboardEvent("keydown", {
597
+ key: "Escape",
598
+ cancelable: true,
599
+ });
600
+ el._handleKeyboard(keyEsc, "t1", "Task 1");
601
+ expect(el._keyboardGrabbedId).toBeNull();
602
+
603
+ // Navigation when not grabbed
604
+ const item1 = document.createElement("div");
605
+ item1.className = "tasks__item";
606
+ item1.setAttribute("tabindex", "0");
607
+ const item2 = document.createElement("div");
608
+ item2.className = "tasks__item";
609
+ item2.setAttribute("tabindex", "0");
610
+ el.shadowRoot?.appendChild(item1);
611
+ el.shadowRoot?.appendChild(item2);
612
+
613
+ const navDown = new KeyboardEvent("keydown", {
614
+ key: "ArrowDown",
615
+ cancelable: true,
616
+ });
617
+ Object.defineProperty(navDown, "target", { value: item1 });
618
+ el._handleKeyboard(navDown, "t1", "Task 1");
619
+
620
+ // 3. Autoscroll
621
+ let content = el.shadowRoot?.querySelector(
622
+ ".tasks__content",
623
+ ) as HTMLElement;
624
+ if (!content) {
625
+ content = document.createElement("div");
626
+ content.className = "tasks__content";
627
+ el.shadowRoot?.appendChild(content);
628
+ }
629
+ content.getBoundingClientRect = () =>
630
+ ({ top: 100, bottom: 500, height: 400 }) as any;
631
+
632
+ el._updateAutoScrollSpeed(110); // Near top
633
+ expect(el._autoScrollActive).toBe(true);
634
+ el._stopAutoScroll();
635
+ expect(el._autoScrollActive).toBe(false);
636
+
637
+ // 4. Focus navigation
638
+ el._focusNext(item1);
639
+ el._focusPrev(item2);
640
+ el._focusNextItem(item1);
641
+ el._focusPrevItem(item2);
642
+
643
+ // 5. Touch events and findTouch
644
+ if (el.shadowRoot) {
645
+ (el.shadowRoot as any).elementFromPoint = jest.fn(() => item1);
646
+ }
647
+ const listEl = document.createElement("div");
648
+ listEl.className = "tasks__list";
649
+ el.shadowRoot?.appendChild(listEl);
650
+ el._bindTouchListEvents(listEl);
651
+
652
+ el._touchId = 123;
653
+ el._touchDraggedTaskId = "t1";
654
+ const fakeTouch = { identifier: 123, clientX: 50, clientY: 150 };
655
+ const touchMoveEvt = new Event("touchmove") as any;
656
+ touchMoveEvt.touches = [fakeTouch];
657
+ touchMoveEvt.changedTouches = [fakeTouch];
658
+ listEl.dispatchEvent(touchMoveEvt);
659
+
660
+ const touchEndEvt = new Event("touchend") as any;
661
+ touchEndEvt.touches = [];
662
+ touchEndEvt.changedTouches = [fakeTouch];
663
+ listEl.dispatchEvent(touchEndEvt);
664
+
665
+ const touchCancelEvt = new Event("touchcancel") as any;
666
+ listEl.dispatchEvent(touchCancelEvt);
667
+
668
+ el.cleanup = jest.fn();
669
+ document.body.removeChild(el);
670
+ });
264
671
  });
265
672
  });
@@ -0,0 +1,58 @@
1
+ import { consumeEscapeSequence } from "./consumeEscapeSequence.js";
2
+
3
+ describe("consumeEscapeSequence", () => {
4
+ it("handles end of string right after escape character", () => {
5
+ const res = consumeEscapeSequence("\x1b", 0);
6
+ expect(res).toEqual({ action: "ignore", nextIndex: 1, incomplete: true });
7
+ });
8
+
9
+ describe("CSI sequences (\\x1b[)", () => {
10
+ it("handles clear-screen sequence ending with J (e.g. \\x1b[2J)", () => {
11
+ const res = consumeEscapeSequence("\x1b[2JHello", 0);
12
+ expect(res).toEqual({ action: "clear-screen", nextIndex: 4 });
13
+ });
14
+
15
+ it("handles clear-line sequence ending with K (e.g. \\x1b[K)", () => {
16
+ const res = consumeEscapeSequence("\x1b[KRest", 0);
17
+ expect(res).toEqual({ action: "clear-line", nextIndex: 3 });
18
+ });
19
+
20
+ it("handles other CSI sequences (e.g. color \\x1b[31m)", () => {
21
+ const res = consumeEscapeSequence("\x1b[31mRed text", 0);
22
+ expect(res).toEqual({ action: "ignore", nextIndex: 5 });
23
+ });
24
+
25
+ it("handles incomplete CSI sequence at end of buffer", () => {
26
+ const res = consumeEscapeSequence("\x1b[31", 0);
27
+ expect(res).toEqual({ action: "ignore", nextIndex: 4, incomplete: true });
28
+ });
29
+ });
30
+
31
+ describe("OSC sequences (\\x1b])", () => {
32
+ it("handles OSC sequence terminated by BEL (\\u0007)", () => {
33
+ const res = consumeEscapeSequence("\x1b]0;Title\u0007Remaining", 0);
34
+ expect(res).toEqual({ action: "ignore", nextIndex: 10 });
35
+ });
36
+
37
+ it("handles OSC sequence terminated by ST (\\u001b\\\\)", () => {
38
+ const res = consumeEscapeSequence("\x1b]0;Title\u001b\\Remaining", 0);
39
+ expect(res).toEqual({ action: "ignore", nextIndex: 11 });
40
+ });
41
+
42
+ it("handles incomplete OSC sequence at end of buffer", () => {
43
+ const res = consumeEscapeSequence("\x1b]0;Incomplete", 0);
44
+ expect(res).toEqual({
45
+ action: "ignore",
46
+ nextIndex: 14,
47
+ incomplete: true,
48
+ });
49
+ });
50
+ });
51
+
52
+ describe("Other escape sequences", () => {
53
+ it("handles simple 2-char escape sequence (e.g. \\x1bM)", () => {
54
+ const res = consumeEscapeSequence("\x1bMRest", 0);
55
+ expect(res).toEqual({ action: "ignore", nextIndex: 2 });
56
+ });
57
+ });
58
+ });
@@ -461,4 +461,84 @@ describe("shadow-claw-tools", () => {
461
461
 
462
462
  document.body.removeChild(el);
463
463
  });
464
+
465
+ it("handles handleAddTool, handleCloneTool, handleRestore, and handleSaveProfile", async () => {
466
+ const el = new ShadowClawTools() as any;
467
+ document.body.appendChild(el);
468
+ await el.connectedCallback();
469
+ const db = {} as any;
470
+
471
+ const dialog = el.shadowRoot?.querySelector(".tools__dialog");
472
+ if (dialog) (dialog as any).close = jest.fn();
473
+ const cloneDialog = el.shadowRoot?.querySelector(".tools__clone-dialog");
474
+ if (cloneDialog) (cloneDialog as any).close = jest.fn();
475
+ const profileDialog = el.shadowRoot?.querySelector(
476
+ ".tools__profile-dialog",
477
+ );
478
+ if (profileDialog) (profileDialog as any).close = jest.fn();
479
+
480
+ // 1. Add Tool validation
481
+ const form = document.createElement("form");
482
+ form.innerHTML = `
483
+ <input name="name" value="" />
484
+ <input name="description" value="" />
485
+ `;
486
+ await el.handleAddTool(db, form);
487
+ expect(toolsStore.addCustomTool).not.toHaveBeenCalled();
488
+
489
+ // 2. Add Tool success
490
+ form.innerHTML = `
491
+ <input name="name" value="unique_tool" />
492
+ <input name="description" value="A unique tool" />
493
+ <input name="input_schema" value='{"type":"object"}' />
494
+ `;
495
+ await el.handleAddTool(db, form);
496
+ expect(toolsStore.addCustomTool).toHaveBeenCalledWith(
497
+ db,
498
+ expect.objectContaining({ name: "unique_tool" }),
499
+ );
500
+
501
+ // 3. Clone Tool
502
+ const cloneForm = document.createElement("form");
503
+ cloneForm.innerHTML = `
504
+ <input name="source" value="bash" />
505
+ <input name="name" value="bash_copy" />
506
+ <input name="description" value="Cloned bash" />
507
+ `;
508
+ await el.handleCloneTool(db, cloneForm);
509
+ expect(toolsStore.cloneTool).toHaveBeenCalledWith(
510
+ db,
511
+ "bash",
512
+ "bash_copy",
513
+ "Cloned bash",
514
+ );
515
+
516
+ // 4. Restore backup
517
+ const restoreFile = {
518
+ text: jest
519
+ .fn<() => Promise<string>>()
520
+ .mockResolvedValue(JSON.stringify({ tools: [] })),
521
+ };
522
+ const restoreInput = {
523
+ files: [restoreFile],
524
+ value: "backup.json",
525
+ } as any;
526
+ await el.handleRestore(db, restoreInput);
527
+ expect(toolsStore.importBackup).toHaveBeenCalled();
528
+
529
+ // 5. Save Profile
530
+ const profileForm = document.createElement("form");
531
+ profileForm.innerHTML = `
532
+ <input name="name" value="Dev Profile" />
533
+ <input name="providerId" value="anthropic" />
534
+ <input name="model" value="claude-3" />
535
+ `;
536
+ await el.handleSaveProfile(db, profileForm);
537
+ expect(toolsStore.addProfile).toHaveBeenCalledWith(
538
+ db,
539
+ expect.objectContaining({ name: "Dev Profile", providerId: "anthropic" }),
540
+ );
541
+
542
+ document.body.removeChild(el);
543
+ });
464
544
  });
@@ -12,6 +12,7 @@ import {
12
12
  DEFAULT_DEV_HOST,
13
13
  DEFAULT_DEV_IP,
14
14
  DEFAULT_DEV_PORT,
15
+ DEFAULT_FILES_UPLOAD_APPEND_ULID,
15
16
  DEFAULT_GROUP_ID,
16
17
  DEFAULT_MAX_TOKENS,
17
18
  DEFAULT_PROMPT_API_BACKEND,
@@ -149,6 +150,10 @@ describe("config.js", () => {
149
150
  expect(DEFAULT_TRANSFORMERS_JS_BACKEND).toBe("auto");
150
151
  expect(DEFAULT_TRANSFORMERS_JS_DTYPE_STRATEGY).toBe("auto");
151
152
  });
153
+
154
+ it("should have valid DEFAULT_FILES_UPLOAD_APPEND_ULID", () => {
155
+ expect(DEFAULT_FILES_UPLOAD_APPEND_ULID).toBe(false);
156
+ });
152
157
  });
153
158
 
154
159
  describe("buildTriggerPattern", () => {
@@ -425,6 +430,10 @@ describe("config.js", () => {
425
430
  );
426
431
  expect(CONFIG_KEYS.CHANNEL_ENABLED_PREFIX).toBe("channel_enabled:");
427
432
  expect(CONFIG_KEYS.CHAT_INPUT_AREA_HEIGHT).toBe("chat_input_area_height");
433
+ expect(CONFIG_KEYS.ENABLED_TOOLS).toBe("enabled_tools");
434
+ expect(CONFIG_KEYS.FILES_UPLOAD_APPEND_ULID).toBe(
435
+ "files_upload_append_ulid",
436
+ );
428
437
  expect(CONFIG_KEYS.GIT_AUTHOR_EMAIL).toBe("git_author_email");
429
438
  expect(CONFIG_KEYS.GIT_AUTHOR_NAME).toBe("git_author_name");
430
439
  expect(CONFIG_KEYS.GIT_CORS_PROXY).toBe("git_cors_proxy");
@@ -1035,6 +1035,10 @@ export const CONFIG_KEYS = {
1035
1035
  CHAT_SPLIT_VIEW_HEIGHT: "chat_split_view_height",
1036
1036
  COMPACTION_ENGINE_PREFERENCE: "compaction_engine_preference",
1037
1037
  CONTEXT_COMPRESSION_ENABLED: "context_compression_enabled",
1038
+ CONTROL_PLANE_CLIENT_ID: "control_plane_client_id",
1039
+ CONTROL_PLANE_ENABLED: "control_plane_enabled",
1040
+ CONTROL_PLANE_TRANSPORT: "control_plane_transport",
1041
+ CONTROL_PLANE_URL: "control_plane_url",
1038
1042
  CONVERSATIONS_HEIGHT: "conversations_height",
1039
1043
  CUSTOM_TOOLS: "custom_tools",
1040
1044
  DB_MIGRATED_FROM_LEGACY: "db_migrated_from_legacy",
@@ -1042,6 +1046,7 @@ export const CONFIG_KEYS = {
1042
1046
  DEFAULT_PINNED_PAGE: "default_pinned_page",
1043
1047
  DIRECT_TOOL_COMMAND_POLICY: "direct_tool_command_policy",
1044
1048
  ENABLED_TOOLS: "enabled_tools",
1049
+ FILES_UPLOAD_APPEND_ULID: "files_upload_append_ulid",
1045
1050
  GIT_ACCOUNTS: "git_accounts",
1046
1051
  GIT_AUTHOR_EMAIL: "git_author_email",
1047
1052
  GIT_AUTHOR_NAME: "git_author_name",
@@ -1073,6 +1078,7 @@ export const CONFIG_KEYS = {
1073
1078
  MESH_LLM_HOST: "mesh_llm_host",
1074
1079
  MODEL: "model",
1075
1080
  OAUTH_PROVIDER_DEFINITIONS: "oauth_provider_definitions",
1081
+ OPFS_MIGRATED_FROM_LEGACY: "opfs_migrated_from_legacy",
1076
1082
  OVERRIDE_PRERENDER_SKELETON: "override_prerender_skeleton",
1077
1083
  PAGES_AUTO_REFRESH_INTERVAL: "pages_auto_refresh_interval",
1078
1084
  PAGES_LIST: "pages_list",
@@ -1180,3 +1186,12 @@ export const DEFAULT_TASK_SERVER_URL = "http://localhost:8888/schedule";
1180
1186
 
1181
1187
  /** Default Task Server Enabled */
1182
1188
  export const DEFAULT_TASK_SERVER_ENABLED = false;
1189
+
1190
+ /** Default Files Upload Append ULID */
1191
+ export const DEFAULT_FILES_UPLOAD_APPEND_ULID = false;
1192
+
1193
+ /** Default Control Plane Enabled (opt-in) */
1194
+ export const DEFAULT_CONTROL_PLANE_ENABLED = false;
1195
+
1196
+ /** Default Control Plane Transport ("sse" or "websocket") */
1197
+ export const DEFAULT_CONTROL_PLANE_TRANSPORT = "sse";