nolo-cli 0.1.40 → 0.1.42

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 (482) hide show
  1. package/README.md +27 -12
  2. package/agent-runtime/agentThread.ts +222 -0
  3. package/agent-runtime/agentThreadAdmission.ts +74 -0
  4. package/agent-runtime/dialogWritePlan.ts +42 -0
  5. package/agent-runtime/hostAdapter.ts +1 -0
  6. package/agent-runtime/hybridRecordStore.ts +5 -3
  7. package/agent-runtime/index.ts +43 -0
  8. package/agent-runtime/localLoop.ts +31 -2
  9. package/agent-runtime/localToolPolicy.ts +1 -0
  10. package/agent-runtime/localWorkspaceTools.ts +1258 -78
  11. package/agent-runtime/noloWorkspaceTools.ts +20 -1
  12. package/agent-runtime/runtimeToolPolicy.ts +1 -5
  13. package/agent-runtime/runtimeToolSurface.ts +1 -0
  14. package/agentAliases.ts +29 -9
  15. package/agentInternalCommandEntries.ts +11 -3
  16. package/agentMachineCommands.ts +1 -0
  17. package/agentRecordCommands.ts +2 -2
  18. package/agentRecordHelpers.ts +59 -6
  19. package/agentRunCommand.ts +84 -19
  20. package/ai/agent/_executeModel.ts +146 -0
  21. package/ai/agent/agentSlice.ts +556 -1
  22. package/ai/agent/appWorkingMemory.ts +126 -0
  23. package/ai/agent/buildEditingContext.ts +378 -0
  24. package/ai/agent/buildSystemPrompt.ts +599 -0
  25. package/ai/agent/canvasEditingContext.ts +49 -0
  26. package/ai/agent/cleanAgentMessages.ts +140 -0
  27. package/ai/agent/cliChatClient.ts +153 -0
  28. package/ai/agent/cliExecutor.ts +264 -46
  29. package/ai/agent/contextCompiler.ts +107 -0
  30. package/ai/agent/contextLayerContract.ts +44 -0
  31. package/ai/agent/createAgentSchema.ts +272 -0
  32. package/ai/agent/currentTimeContext.ts +65 -0
  33. package/ai/agent/executeToolCall.ts +58 -0
  34. package/ai/agent/fetchAgentContexts.ts +42 -0
  35. package/ai/agent/generatePrompt.ts +3 -0
  36. package/ai/agent/getFullChatContextKeys.ts +168 -0
  37. package/ai/agent/guidedCreation/capabilities.ts +84 -0
  38. package/ai/agent/guidedCreation/types.ts +38 -0
  39. package/ai/agent/isLiveAudioOnlyAgent.ts +26 -0
  40. package/ai/agent/liveAudioModel.ts +2 -0
  41. package/ai/agent/pageBuilderHandoffRules.ts +75 -0
  42. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  43. package/ai/agent/planSlice.ts +259 -0
  44. package/ai/agent/referenceUtils.ts +229 -0
  45. package/ai/agent/runAgentBackground.ts +310 -0
  46. package/ai/agent/runAgentClientLoop.ts +164 -0
  47. package/ai/agent/runtimeGuidance.ts +116 -0
  48. package/ai/agent/serverOrigin.ts +7 -0
  49. package/ai/agent/startParallelAgentStreams.ts +424 -0
  50. package/ai/agent/startupProtocol.ts +53 -0
  51. package/ai/agent/streamAgentChatTurn.ts +2135 -0
  52. package/ai/agent/streamAgentChatTurnUtils.ts +923 -0
  53. package/ai/agent/toolFailureGuard.ts +99 -0
  54. package/ai/agent/types.ts +73 -0
  55. package/ai/agent/utils/imageOutput.ts +37 -0
  56. package/ai/agent/utils/publicImageAgentMode.ts +26 -0
  57. package/ai/ai.locale.ts +1165 -0
  58. package/ai/chat/accumulateToolCallChunks.ts +126 -0
  59. package/ai/chat/fetchUtils.ts +143 -0
  60. package/ai/chat/inlineImageUrlsForCustomProvider.ts +122 -0
  61. package/ai/chat/parseApiError.ts +67 -0
  62. package/ai/chat/parseMultilineSSE.ts +95 -0
  63. package/ai/chat/sendOpenAICompletionsRequest.ts +921 -0
  64. package/ai/chat/sendOpenAIResponseRequest.ts +720 -0
  65. package/ai/chat/serverProxyRetry.ts +102 -0
  66. package/ai/chat/shouldUseServerProxy.ts +18 -0
  67. package/ai/chat/streamRetry.ts +46 -0
  68. package/ai/chat/updateTotalUsage.ts +89 -0
  69. package/ai/context/buildReferenceContext.ts +437 -0
  70. package/ai/context/retention.ts +165 -0
  71. package/ai/context/tokenUtils.ts +78 -0
  72. package/ai/llm/agentCapabilities.ts +74 -0
  73. package/ai/llm/deepinfra.ts +79 -0
  74. package/ai/llm/fireworks.ts +68 -0
  75. package/ai/llm/generateRequestBody.ts +165 -0
  76. package/ai/llm/getModelContextWindow.ts +93 -0
  77. package/ai/llm/getPricing.ts +252 -0
  78. package/ai/llm/imagePricing.ts +66 -0
  79. package/ai/llm/isResponseAPIModel.ts +13 -0
  80. package/ai/llm/kimi.ts +22 -0
  81. package/ai/llm/mimo.ts +99 -0
  82. package/ai/llm/mistral.ts +21 -0
  83. package/ai/llm/models.ts +45 -0
  84. package/ai/llm/openrouterModels.ts +31 -0
  85. package/ai/llm/providers.ts +313 -0
  86. package/ai/llm/reasoningModels.ts +27 -0
  87. package/ai/llm/types.ts +82 -0
  88. package/ai/llm/usageRequestOptions.ts +59 -0
  89. package/ai/memory/policy.ts +49 -0
  90. package/ai/memory/queryShared.ts +167 -0
  91. package/ai/memory/recentRelationshipRecap.ts +251 -0
  92. package/ai/memory/remember.ts +111 -0
  93. package/ai/memory/scope.ts +74 -0
  94. package/ai/memory/store.ts +20 -0
  95. package/ai/memory/storeShared.ts +76 -0
  96. package/ai/memory/types.ts +52 -0
  97. package/ai/memory/understanding.ts +341 -0
  98. package/ai/memory/understandingGreeting.ts +264 -0
  99. package/ai/policy/personalizationDialog.ts +333 -0
  100. package/ai/policy/runtimePolicy.ts +526 -0
  101. package/ai/policy/selfUpdateFields.ts +48 -0
  102. package/ai/policy/types.ts +64 -0
  103. package/ai/skills/referenceRuntime.ts +274 -0
  104. package/ai/skills/skillDiagnostics.ts +251 -0
  105. package/ai/skills/skillDocBuilder.ts +139 -0
  106. package/ai/skills/skillSummaryMarker.ts +26 -0
  107. package/ai/token/calculatePrice.ts +576 -0
  108. package/ai/token/missingUsageEstimate.ts +42 -0
  109. package/ai/token/normalizeUsage.ts +124 -0
  110. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  111. package/ai/token/prepareTokenUsageData.ts +93 -0
  112. package/ai/token/resolveBillingTarget.ts +52 -0
  113. package/ai/token/saveTokenRecord.ts +53 -0
  114. package/ai/token/types.ts +103 -0
  115. package/ai/tools/agent/agentTools.ts +214 -0
  116. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  117. package/ai/tools/agent/callAgentTool.ts +139 -0
  118. package/ai/tools/agent/createAgentTool.ts +512 -0
  119. package/ai/tools/agent/createDialogTool.ts +69 -0
  120. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  121. package/ai/tools/agent/parallelBudget.ts +221 -0
  122. package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
  123. package/ai/tools/agent/presets/appBuilderPreset.ts +154 -0
  124. package/ai/tools/agent/runLlmTool.ts +96 -0
  125. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  126. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  127. package/ai/tools/agent/startAgentDialogTool.ts +54 -0
  128. package/ai/tools/agent/streamParallelAgentsTool.ts +123 -0
  129. package/ai/tools/agent/updateAgentTool.ts +96 -0
  130. package/ai/tools/agent/updateSelfTool.ts +113 -0
  131. package/ai/tools/amazonProductScraperTool.ts +86 -0
  132. package/ai/tools/apifyActorClient.ts +45 -0
  133. package/ai/tools/appEditGuard.ts +372 -0
  134. package/ai/tools/appReadSnapshot.ts +153 -0
  135. package/ai/tools/appTools.ts +1549 -0
  136. package/ai/tools/applyEditTool.ts +259 -0
  137. package/ai/tools/applyLineEditsTool.ts +315 -0
  138. package/ai/tools/browserTools/click.ts +33 -0
  139. package/ai/tools/browserTools/closeSession.ts +29 -0
  140. package/ai/tools/browserTools/common.ts +27 -0
  141. package/ai/tools/browserTools/openSession.ts +48 -0
  142. package/ai/tools/browserTools/readContent.ts +38 -0
  143. package/ai/tools/browserTools/selectOption.ts +46 -0
  144. package/ai/tools/browserTools/typeText.ts +42 -0
  145. package/ai/tools/category/createCategoryTool.ts +66 -0
  146. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  147. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  148. package/ai/tools/cfBrowserTools.ts +319 -0
  149. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  150. package/ai/tools/checkEnvTool.ts +102 -0
  151. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  152. package/ai/tools/codeSearchTool.ts +123 -0
  153. package/ai/tools/codeTools.ts +101 -0
  154. package/ai/tools/convertMarxistsBookTool.ts +103 -0
  155. package/ai/tools/createDocTool.ts +132 -0
  156. package/ai/tools/createSkillDocTool.ts +155 -0
  157. package/ai/tools/createWorkflowTool.ts +154 -0
  158. package/ai/tools/delayTool.ts +31 -0
  159. package/ai/tools/deleteSpacesTool.ts +325 -0
  160. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  161. package/ai/tools/devReloadUtils.ts +29 -0
  162. package/ai/tools/dialogMessageSearch.ts +137 -0
  163. package/ai/tools/doctorSkillTool.ts +72 -0
  164. package/ai/tools/ecommerceScraperTool.ts +86 -0
  165. package/ai/tools/emailTools.ts +549 -0
  166. package/ai/tools/evalSkillTool.ts +92 -0
  167. package/ai/tools/exaSearchTool.ts +63 -0
  168. package/ai/tools/execShellTool.ts +266 -0
  169. package/ai/tools/executeSqlTool.ts +192 -0
  170. package/ai/tools/fetchWebpageSupport.ts +333 -0
  171. package/ai/tools/fetchWebpageTool.ts +97 -0
  172. package/ai/tools/geminiImagePreviewTool.ts +386 -0
  173. package/ai/tools/generateDocxTool.ts +215 -0
  174. package/ai/tools/googleSearchScraperTool.ts +106 -0
  175. package/ai/tools/importDataTool.ts +133 -0
  176. package/ai/tools/importSkillTool.ts +162 -0
  177. package/ai/tools/index.ts +2290 -0
  178. package/ai/tools/jdProductScraperTool.ts +821 -0
  179. package/ai/tools/listUserSpacesTool.ts +113 -0
  180. package/ai/tools/localFilesTool.ts +276 -0
  181. package/ai/tools/marxistsOfflineBook.ts +253 -0
  182. package/ai/tools/modelUsageTools.ts +206 -0
  183. package/ai/tools/noloWorkspaceReadTools.ts +467 -0
  184. package/ai/tools/olmOcrTool.ts +33 -0
  185. package/ai/tools/openaiImageTool.ts +267 -0
  186. package/ai/tools/prepareTools.ts +193 -0
  187. package/ai/tools/readDocTool.ts +88 -0
  188. package/ai/tools/readFileTool.ts +214 -0
  189. package/ai/tools/readTool.ts +194 -0
  190. package/ai/tools/readXPostTool.ts +229 -0
  191. package/ai/tools/readXhsProfileTool.ts +638 -0
  192. package/ai/tools/rememberMemoryTool.ts +84 -0
  193. package/ai/tools/remotionVideoTool.ts +151 -0
  194. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  195. package/ai/tools/searchWorkspaceTool.ts +259 -0
  196. package/ai/tools/skillFollowup.ts +86 -0
  197. package/ai/tools/surfWeatherTool.ts +169 -0
  198. package/ai/tools/table/addTableRowTool.ts +217 -0
  199. package/ai/tools/table/createTableTool.ts +326 -0
  200. package/ai/tools/table/rowTools.ts +386 -0
  201. package/ai/tools/table/schemaTools.ts +244 -0
  202. package/ai/tools/table/shareTableTool.ts +148 -0
  203. package/ai/tools/table/toolShared.ts +129 -0
  204. package/ai/tools/taobaoTmallProductScraperTool.ts +49 -0
  205. package/ai/tools/toolApiClient.ts +279 -0
  206. package/ai/tools/toolPacks.ts +19 -0
  207. package/ai/tools/toolResultError.ts +42 -0
  208. package/ai/tools/toolRunSlice.ts +303 -0
  209. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  210. package/ai/tools/uiAskChoiceTool.ts +104 -0
  211. package/ai/tools/updateContentTitleTool.ts +84 -0
  212. package/ai/tools/updateDocTool.ts +105 -0
  213. package/ai/tools/updateUserPreferenceProfileTool.ts +153 -0
  214. package/ai/tools/wereadGatewayTool.ts +152 -0
  215. package/ai/tools/whisperTool.ts +76 -0
  216. package/ai/tools/writeFileTool.ts +213 -0
  217. package/ai/tools/youtubeScraperTool.ts +116 -0
  218. package/ai/tools/ziweiChartTool.ts +678 -0
  219. package/ai/types.ts +58 -0
  220. package/ai/workflow/workflowExecutor.ts +323 -0
  221. package/ai/workflow/workflowSlice.ts +73 -0
  222. package/ai/workflow/workflowTypes.ts +106 -0
  223. package/app/actions/syncAppRecord.ts +73 -0
  224. package/app/constants/animationSets.ts +21 -0
  225. package/app/constants/appEditor.ts +99 -0
  226. package/app/favorite/deletedFavoriteProjection.ts +31 -0
  227. package/app/favorite/favoriteSlice.ts +690 -0
  228. package/app/favorite/type.ts +43 -0
  229. package/app/hooks/deleteDbKey.ts +254 -0
  230. package/app/i18n/client.ts +13 -0
  231. package/app/i18n/i18n.base.ts +14 -0
  232. package/app/i18n/i18n.config.ts +77 -0
  233. package/app/i18n/index.ts +15 -0
  234. package/app/i18n/mapLanguage.ts +11 -0
  235. package/app/i18n/translations/appBuilder.locale.ts +453 -0
  236. package/app/i18n/translations/error.locale.ts +246 -0
  237. package/app/i18n/translations/interface.locale.ts +2206 -0
  238. package/app/i18n/translations/pricing.locale.ts +397 -0
  239. package/app/i18n/translations/privacy.locale.ts +168 -0
  240. package/app/i18n/translations/seo.locale.ts +129 -0
  241. package/app/i18n/translations/terms.locale.ts +124 -0
  242. package/app/i18n/types.ts +6 -0
  243. package/app/notifications/model.ts +77 -0
  244. package/app/notifications/notificationSlice.ts +141 -0
  245. package/app/reducer.ts +36 -0
  246. package/app/settings/dbActionThunks.ts +34 -0
  247. package/app/settings/defaultSpacePreference.ts +87 -0
  248. package/app/settings/settingSlice.tsx +1212 -0
  249. package/app/settings/settingsRecord.ts +8 -0
  250. package/app/stateViews/runtime.ts +90 -0
  251. package/app/store.ts +55 -0
  252. package/app/theme/colors.ts +280 -0
  253. package/app/theme/fontPreference.ts +77 -0
  254. package/app/theme/theme.config.ts +120 -0
  255. package/app/theme/themeModeBootstrap.ts +123 -0
  256. package/app/types/appSummary.ts +118 -0
  257. package/app/types.ts +358 -0
  258. package/app/utils/appKeys.ts +55 -0
  259. package/app/utils/async.ts +28 -0
  260. package/app/utils/desktopAgentRuntimeTurnClient.ts +75 -0
  261. package/app/utils/env.ts +17 -0
  262. package/app/utils/fileUtils.ts +229 -0
  263. package/app/utils/imageUtils.ts +274 -0
  264. package/app/utils/myContentItems.ts +289 -0
  265. package/app/utils/retryAfter.ts +36 -0
  266. package/app/utils/retryFetch.ts +61 -0
  267. package/app/utils/toast.ts +3 -0
  268. package/auth/action/signUpAction.ts +227 -0
  269. package/auth/adminPermissions.ts +78 -0
  270. package/auth/authSlice.ts +531 -0
  271. package/auth/client/loginRequest.ts +14 -0
  272. package/auth/resetAuthScopedClientState.ts +27 -0
  273. package/auth/routes.ts +183 -0
  274. package/auth/token.ts +63 -0
  275. package/auth/types.ts +40 -0
  276. package/chat/chat.locale.ts +817 -0
  277. package/chat/dialog/actions/addCybotAction.ts +52 -0
  278. package/chat/dialog/actions/addReferenceKeysAction.ts +62 -0
  279. package/chat/dialog/actions/builtinDialogLlm.ts +35 -0
  280. package/chat/dialog/actions/cleanupCliSession.ts +40 -0
  281. package/chat/dialog/actions/createDialogAction.ts +379 -0
  282. package/chat/dialog/actions/createScheduledTaskAction.ts +78 -0
  283. package/chat/dialog/actions/handleSendMessageAction.ts +202 -0
  284. package/chat/dialog/actions/handleSendMessageResolver.ts +27 -0
  285. package/chat/dialog/actions/removeCybotAction.ts +6 -0
  286. package/chat/dialog/actions/removeDialogAgentAction.ts +44 -0
  287. package/chat/dialog/actions/replacePrimaryCybotAction.ts +6 -0
  288. package/chat/dialog/actions/setPrimaryDialogAgentAction.ts +52 -0
  289. package/chat/dialog/actions/updateDialogSummaryAction.ts +430 -0
  290. package/chat/dialog/actions/updateDialogTitleAction.ts +151 -0
  291. package/chat/dialog/actions/updateDialogTitlePolicy.ts +30 -0
  292. package/chat/dialog/actions/updateTokensAction.ts +233 -0
  293. package/chat/dialog/dialogAgents.ts +72 -0
  294. package/chat/dialog/dialogAttachmentCleanup.ts +135 -0
  295. package/chat/dialog/dialogSlice.ts +985 -0
  296. package/chat/dialog/dialogTitle.ts +81 -0
  297. package/chat/dialog/dialogTokenStats.ts +12 -0
  298. package/chat/dialog/dialogUrl.ts +6 -0
  299. package/chat/dialog/goal.ts +108 -0
  300. package/chat/dialog/objectAssistantRegistry.ts +286 -0
  301. package/chat/dialog/schedule.ts +17 -0
  302. package/chat/messages/activityCompletion.ts +97 -0
  303. package/chat/messages/fetchAndCacheMessages.ts +119 -0
  304. package/chat/messages/fetchConvMsgs.ts +51 -0
  305. package/chat/messages/fileUrl.ts +13 -0
  306. package/chat/messages/imagePayloadPersistence.ts +22 -0
  307. package/chat/messages/messageAgent.ts +8 -0
  308. package/chat/messages/messageContent.ts +203 -0
  309. package/chat/messages/messageContract.ts +88 -0
  310. package/chat/messages/messageSlice.ts +1386 -0
  311. package/chat/messages/toolThunks.ts +646 -0
  312. package/chat/messages/types.ts +8 -1
  313. package/chat/messages/web/toolDisplayName.ts +686 -0
  314. package/cli/agentAliases.ts +29 -9
  315. package/cli/agentInternalCommandEntries.ts +11 -3
  316. package/cli/agentMachineCommands.ts +1 -0
  317. package/cli/agentRecordCommands.ts +2 -2
  318. package/cli/agentRecordHelpers.ts +59 -6
  319. package/cli/agentRunCommand.ts +84 -19
  320. package/cli/client/agentRun.ts +92 -261
  321. package/cli/client/localRuntimeAdapter.ts +292 -50
  322. package/cli/commandRegistry.ts +7 -4
  323. package/cli/dialogAttachmentCleanup.ts +151 -0
  324. package/cli/dialogCommands.ts +163 -61
  325. package/cli/dialogInternalCommandEntries.ts +1 -1
  326. package/cli/docCreateCommands.ts +345 -0
  327. package/cli/docInternalCommandEntries.ts +11 -0
  328. package/cli/docPageHelpers.ts +80 -0
  329. package/cli/docSkillHelpers.ts +67 -0
  330. package/cli/docSpaceHelpers.ts +46 -0
  331. package/cli/internalCommandEntries.ts +4 -0
  332. package/cli/machineCommands.ts +18 -34
  333. package/cli/memoryCommands.ts +253 -0
  334. package/cli/memoryInternalCommandEntries.ts +9 -0
  335. package/cli/qoderUsageProbe.ts +252 -0
  336. package/cli/scriptCommandEntries.ts +0 -3
  337. package/cli/secretScan.ts +31 -0
  338. package/cli/tableCommands.ts +248 -13
  339. package/cli/workflowInternalCommandEntries.ts +2 -3
  340. package/client/agentRun.test.ts +119 -83
  341. package/client/agentRun.ts +92 -261
  342. package/client/localRuntimeAdapter.test.ts +894 -32
  343. package/client/localRuntimeAdapter.ts +292 -50
  344. package/commandRegistry.ts +7 -4
  345. package/connector-experimental/capabilities.ts +10 -9
  346. package/core/builtinAgents.ts +209 -0
  347. package/core/config.ts +15 -0
  348. package/core/crypto.ts +51 -0
  349. package/core/generateKeyPairFromSeedV1.ts +15 -0
  350. package/core/generateMainKey.ts +22 -0
  351. package/core/init.ts +41 -0
  352. package/core/localOrigins.ts +8 -0
  353. package/core/password.ts +13 -0
  354. package/core/prefix.ts +23 -0
  355. package/create/editor/transforms/fromMarkdown/index.ts +260 -0
  356. package/create/editor/transforms/fromMarkdown/inline.ts +285 -0
  357. package/create/editor/transforms/fromMarkdown/table.ts +130 -0
  358. package/create/editor/transforms/markdownToSlate.ts +24 -0
  359. package/create/editor/transforms/slateToRenderMarkdown.ts +218 -0
  360. package/create/editor/transforms/slateToSimplifiedMarkdown.ts +224 -0
  361. package/create/editor/transforms/slateToText.ts +33 -0
  362. package/create/editor/utils/slateUtils.ts +233 -0
  363. package/create/space/addSpaceAction.ts +193 -0
  364. package/create/space/category/categoryActions.ts +427 -0
  365. package/create/space/changeSpaceAction.ts +36 -0
  366. package/create/space/constants.ts +8 -0
  367. package/create/space/content/addContentAction.ts +116 -0
  368. package/create/space/content/contentThunks.ts +90 -0
  369. package/create/space/content/deleteContentFromSpaceAction.ts +187 -0
  370. package/create/space/content/deleteMultipleContentAction.ts +112 -0
  371. package/create/space/content/moveContentAction.ts +285 -0
  372. package/create/space/content/updateContentCategoryAction.ts +154 -0
  373. package/create/space/content/updateContentTitleAction.ts +151 -0
  374. package/create/space/content/uploadAndAddFileToSpaceAction.ts +98 -0
  375. package/create/space/contentKeyUtils.ts +117 -0
  376. package/create/space/deleteSpaceAction.ts +276 -0
  377. package/create/space/fetchSpaceAction.ts +70 -0
  378. package/create/space/fetchSpaceSidebarStateAction.ts +26 -0
  379. package/create/space/loadDefaultSpaceAction.ts +57 -0
  380. package/create/space/member/addMemberAction.ts +79 -0
  381. package/create/space/member/fetchUserSpaceMembershipsAction.ts +386 -0
  382. package/create/space/member/memberThunks.ts +47 -0
  383. package/create/space/member/removeMemberAction.ts +67 -0
  384. package/create/space/resolvePreferredSpaceId.ts +113 -0
  385. package/create/space/sidebarVisibleTypes.ts +260 -0
  386. package/create/space/space.locale.ts +1087 -0
  387. package/create/space/spaceAccess.ts +182 -0
  388. package/create/space/spaceCollapsedState.ts +54 -0
  389. package/create/space/spaceKeys.ts +65 -0
  390. package/create/space/spaceSlice.ts +328 -0
  391. package/create/space/spaceThunks.ts +410 -0
  392. package/create/space/types.ts +27 -0
  393. package/create/space/updateSpaceAction.ts +107 -0
  394. package/create/space/utils/permissions.ts +47 -0
  395. package/create/types.ts +21 -0
  396. package/database/actions/agentReadResolution.ts +33 -0
  397. package/database/actions/common.ts +243 -0
  398. package/database/actions/deleteFile.ts +40 -0
  399. package/database/actions/fileContent.ts +125 -0
  400. package/database/actions/patch.ts +155 -0
  401. package/database/actions/read.ts +358 -0
  402. package/database/actions/readAndWait.ts +260 -0
  403. package/database/actions/readRequestManager.ts +120 -0
  404. package/database/actions/readResolution.ts +125 -0
  405. package/database/actions/remove.ts +94 -0
  406. package/database/actions/replication.ts +477 -0
  407. package/database/actions/upload.ts +186 -0
  408. package/database/actions/upsert.ts +56 -0
  409. package/database/actions/write.ts +146 -0
  410. package/database/authority/ownerKey.ts +80 -0
  411. package/database/authority/recordAuthority.ts +67 -0
  412. package/database/authority/userAuthorityRegistry.ts +54 -0
  413. package/database/client/fetchUserData.ts +61 -0
  414. package/database/dbActionThunks.ts +1 -0
  415. package/database/dbSlice.ts +149 -0
  416. package/database/fileRing.ts +51 -0
  417. package/database/fileStorage.ts +232 -0
  418. package/database/fileUrl.ts +34 -0
  419. package/database/keys.ts +767 -0
  420. package/database/queryPrefixes.ts +14 -0
  421. package/database/requests.ts +443 -0
  422. package/database/runtimeServerContext.ts +54 -0
  423. package/database/tenantPlacement.ts +57 -0
  424. package/database/userPreferenceRegister.ts +131 -0
  425. package/database/utils/ulid.ts +1 -0
  426. package/dialogAttachmentCleanup.ts +151 -0
  427. package/dialogCommands.ts +163 -61
  428. package/dialogInternalCommandEntries.ts +1 -1
  429. package/docCreateCommands.ts +345 -0
  430. package/docInternalCommandEntries.ts +11 -0
  431. package/docPageHelpers.ts +80 -0
  432. package/docSkillHelpers.ts +67 -0
  433. package/docSpaceHelpers.ts +46 -0
  434. package/integrations/anthropic/anthropicModels.ts +47 -0
  435. package/integrations/deepseek/models.ts +37 -0
  436. package/integrations/google/models.ts +341 -0
  437. package/integrations/openai/chatCompletionStreamMode.ts +10 -0
  438. package/integrations/openai/filterAndCleanMessages.ts +284 -0
  439. package/integrations/openai/flexTier.ts +102 -0
  440. package/integrations/openai/generateOpenAIRequestBody.ts +271 -0
  441. package/integrations/openai/generateResponseRequestBody.ts +77 -0
  442. package/integrations/openai/models.ts +271 -0
  443. package/integrations/openai/providerBodyCompatibility.ts +23 -0
  444. package/integrations/openai/responsesHelpers.ts +297 -0
  445. package/integrations/x-reader/types.ts +109 -0
  446. package/integrations/xai/models.ts +13 -0
  447. package/integrations/xhs-reader/redaction.ts +68 -0
  448. package/integrations/xhs-reader/types.ts +277 -0
  449. package/integrations/xhs-reader/url.ts +80 -0
  450. package/internalCommandEntries.ts +4 -0
  451. package/machineCommands.ts +18 -34
  452. package/memoryCommands.ts +253 -0
  453. package/memoryInternalCommandEntries.ts +9 -0
  454. package/package.json +454 -9
  455. package/qoderUsageProbe.ts +252 -0
  456. package/render/contentIcon/types.ts +167 -0
  457. package/render/page/createPageAction.ts +183 -0
  458. package/render/page/docSlice.ts +495 -0
  459. package/render/page/types.ts +19 -0
  460. package/render/table/createTableAction.ts +274 -0
  461. package/render/table/deleteTableAction.ts +193 -0
  462. package/render/table/fetchAndCacheTableRows.ts +174 -0
  463. package/render/table/tableSlice.ts +1167 -0
  464. package/render/table/toolValueUtils.ts +363 -0
  465. package/render/table/types.ts +262 -0
  466. package/render/table/utils/tableSerialization.ts +50 -0
  467. package/scriptCommandEntries.ts +0 -3
  468. package/secretScan.ts +31 -0
  469. package/server/handlers/agentRun/types.ts +105 -0
  470. package/share/action.ts +225 -0
  471. package/share/helpers.ts +302 -0
  472. package/share/keys.ts +243 -0
  473. package/share/publicSharePolicy.ts +27 -0
  474. package/share/shareSlice.ts +53 -0
  475. package/share/types.ts +100 -0
  476. package/tableCommands.ts +248 -13
  477. package/workflowInternalCommandEntries.ts +2 -3
  478. package/ai/tools/agent/taskRunTool.ts +0 -112
  479. package/cli/client/taskRunPrompt.ts +0 -27
  480. package/cli/taskRunCommand.ts +0 -265
  481. package/client/taskRunPrompt.ts +0 -27
  482. package/taskRunCommand.ts +0 -265
@@ -0,0 +1,1212 @@
1
+ // app/settings/settingSlice.ts
2
+
3
+ import {
4
+ buildCreateSlice,
5
+ asyncThunkCreator,
6
+ createSelector,
7
+ type PayloadAction,
8
+ } from "@reduxjs/toolkit";
9
+ import type { RootState } from "../store";
10
+ import { isProduction } from "../utils/env";
11
+ import { getIsDesktopApp } from "../utils/env";
12
+ import { getAllServers } from "../../database/actions/common";
13
+ import { SERVERS } from "../../database/config";
14
+ import { createUserKey, createUserPreferenceKey } from "../../database/keys";
15
+ import { selectUserId } from "../../auth/authSlice";
16
+ import {
17
+ USER_PREFERENCE_NAMES,
18
+ buildDefaultAgentPreferenceRegisterRecord,
19
+ readUserPreferenceRegisterValue,
20
+ } from "../../database/userPreferenceRegister";
21
+
22
+ // 从分离的配置文件导入静态主题数据
23
+ import {
24
+ DEFAULT_THEME_NAME,
25
+ SPACE,
26
+ THEME_COLORS,
27
+ MODE_COLORS,
28
+ } from "../theme/theme.config";
29
+ import {
30
+ normalizeThemeName,
31
+ resolveThemeModeIsDark,
32
+ SYSTEM_DARK_MEDIA_QUERY,
33
+ } from "../theme/themeModeBootstrap";
34
+ import {
35
+ DEFAULT_FONT_PRESET,
36
+ FONT_PRESET_CSS_VARIABLES,
37
+ type FontPreset,
38
+ normalizeFontPreset,
39
+ } from "../theme/fontPreference";
40
+ import { legacyNoloAgentId, noloAgentId } from "../../core/init";
41
+ import { DEFAULT_ANIMATION_SET_INDEX } from "../constants/animationSets";
42
+ import {
43
+ areSidebarVisibleTypesEqual,
44
+ DEFAULT_SIDEBAR_VISIBLE_TYPES,
45
+ LEGACY_DEFAULT_SIDEBAR_VISIBLE_TYPES,
46
+ normalizeSidebarVisibleTypes,
47
+ type SidebarVisibleType,
48
+ } from "../../create/space/sidebarVisibleTypes";
49
+ import { normalizeSpaceId } from "../../create/space/spaceKeys";
50
+ import {
51
+ DEFAULT_USER_PREFERENCE_PROFILE,
52
+ type KnowledgeCaptureLevel,
53
+ type SpaceContextLevel,
54
+ type TonePreset,
55
+ } from "../../ai/policy/types";
56
+ import {
57
+ DEFAULT_AUTO_APPROVED_SELF_UPDATE_FIELDS,
58
+ normalizeAgentUpdateFieldList,
59
+ type AgentUpdateField,
60
+ } from "../../ai/policy/selfUpdateFields";
61
+ import { getSettingDbActionThunks } from "./dbActionThunks";
62
+ import {
63
+ normalizeDefaultSpaceIdPreference,
64
+ persistDefaultSpacePreference,
65
+ readDefaultSpaceIdPreference,
66
+ } from "./defaultSpacePreference";
67
+ import { withSettingsRecordSchema } from "./settingsRecord";
68
+
69
+ // --- State 定义 (包含所有字段) ---
70
+ interface SettingState {
71
+ isAutoSync: boolean;
72
+ currentServer: string;
73
+ defaultSpaceId?: string | null;
74
+ syncServers: string[];
75
+ showThinking: boolean;
76
+ preferredAnimationSet: number;
77
+ maxExecutionTime: number;
78
+ maxCost: number;
79
+ themeName: keyof typeof THEME_COLORS;
80
+ themeMode: "system" | "light" | "dark";
81
+ isDark: boolean; // resolved value: managed by setThemeMode + useSystemTheme
82
+ sidebarWidth: number;
83
+ headerHeight: number;
84
+ density: "compact" | "spacious";
85
+ fontPreset: FontPreset;
86
+
87
+ // 编辑器配置
88
+ editorDefaultMode: "markdown" | "block";
89
+ editorLightCodeTheme: string;
90
+ editorDarkCodeTheme: string;
91
+ editorWordCountEnabled: boolean;
92
+ editorShortcuts: {
93
+ heading: boolean;
94
+ ulist: boolean;
95
+ olist: boolean;
96
+ quote: boolean;
97
+ code: boolean;
98
+ tasklist: boolean;
99
+ };
100
+ editorFontSize: number;
101
+ editorAutoSave: boolean;
102
+ editorAutoSaveInterval: number;
103
+ editorLineNumbers: boolean;
104
+ editorWordWrap: boolean;
105
+ editorSpellCheck: boolean;
106
+ editorTabSize: number;
107
+ editorFontFamily: string;
108
+
109
+ // 是否允许读取当前空间内容作为上下文
110
+ enableReadCurrentSpace: boolean;
111
+
112
+ // 侧边栏默认显示的内容类型
113
+ sidebarVisibleTypes: SidebarVisibleType[];
114
+
115
+ // 通用提示词
116
+ globalPrompt: string;
117
+
118
+ // 用户偏好的语气 preset(尽量通用,不覆盖 agent 自身人格)
119
+ userTonePreset: TonePreset;
120
+
121
+ // AI 对知识沉淀(doc/table)的主动程度
122
+ knowledgeCaptureLevel: KnowledgeCaptureLevel;
123
+
124
+ // AI 使用当前空间作为上下文的积极程度
125
+ spaceContextLevel: SpaceContextLevel;
126
+
127
+ // updateSelf 默认哪些字段不再询问
128
+ autoApproveSelfUpdateFields: AgentUpdateField[];
129
+
130
+ // ADDED: AI Recent Content Limit
131
+ aiRecentContentLimit: number;
132
+
133
+ // 上下文保留程度 1-100(默认 50)
134
+ // 值越高 = 保留越多历史上下文,新对话空间越小
135
+ contextRetention: number;
136
+
137
+ // 默认启动的智能体 ID
138
+ defaultAgentId?: string;
139
+
140
+ // PDF OCR 模型选择("none" 表示不使用 OCR,用 pdf.js 提取文本)
141
+ ocrModel: "none" | "olm_ocr";
142
+
143
+ // 对话页面快捷滚动按钮
144
+ showScrollToTopButton: boolean;
145
+ showScrollToBottomButton: boolean;
146
+ createMenuOpenCount: number;
147
+
148
+ [key: string]: any;
149
+ }
150
+
151
+ export const SYSTEM_DEFAULT_AGENT_ID = "system-default";
152
+
153
+ // --- 初始状态 (包含所有字段的默认值) ---
154
+ const initialState: SettingState = {
155
+ isAutoSync: false,
156
+ currentServer: isProduction ? SERVERS.MAIN : SERVERS.US,
157
+ defaultSpaceId: null,
158
+ syncServers: Object.values(SERVERS),
159
+ showThinking: true,
160
+ preferredAnimationSet: DEFAULT_ANIMATION_SET_INDEX,
161
+ maxExecutionTime: 600_000,
162
+ maxCost: 1,
163
+ themeName: DEFAULT_THEME_NAME,
164
+ themeMode: "system" as const,
165
+ isDark: false,
166
+ sidebarWidth: 320,
167
+ headerHeight: 56,
168
+ density: "compact" as const,
169
+ fontPreset: DEFAULT_FONT_PRESET,
170
+ editorDefaultMode: "markdown",
171
+ editorLightCodeTheme: "default",
172
+ editorDarkCodeTheme: "okaidia",
173
+ editorWordCountEnabled: true,
174
+ editorShortcuts: {
175
+ heading: true,
176
+ ulist: true,
177
+ olist: true,
178
+ quote: true,
179
+ code: true,
180
+ tasklist: true,
181
+ },
182
+ editorFontSize: 14,
183
+ editorAutoSave: true,
184
+ editorAutoSaveInterval: 30,
185
+ editorLineNumbers: false,
186
+ editorWordWrap: true,
187
+ editorSpellCheck: true,
188
+ editorTabSize: 2,
189
+ editorFontFamily: "SF Mono, Monaco, Cascadia Code, Roboto Mono, monospace",
190
+ enableReadCurrentSpace: true,
191
+ sidebarVisibleTypes: [...DEFAULT_SIDEBAR_VISIBLE_TYPES],
192
+ globalPrompt:
193
+ "以下是关于我的通用说明,请在任意场景下都以此来理解和服务我:\n" +
194
+ "1. 我希望你用清晰、简洁的方式回答问题,并在必要时给出示例。\n" +
195
+ "2. 如果有多种解决方案,请先给出推荐方案,再简要说明其他方案的优缺点。\n" +
196
+ "3. 当你不确定答案时,请明确说明不确定,并推测可能的方向,而不是编造事实。\n" +
197
+ "4. 如果涉及代码,请优先使用现代、通用的最佳实践。",
198
+ userTonePreset: DEFAULT_USER_PREFERENCE_PROFILE.tone?.preset ?? "default",
199
+ knowledgeCaptureLevel: DEFAULT_USER_PREFERENCE_PROFILE.knowledgeCaptureLevel,
200
+ spaceContextLevel: DEFAULT_USER_PREFERENCE_PROFILE.spaceContextLevel,
201
+ autoApproveSelfUpdateFields: [...DEFAULT_AUTO_APPROVED_SELF_UPDATE_FIELDS],
202
+ aiRecentContentLimit: 50,
203
+ contextRetention: 50,
204
+ defaultAgentId: SYSTEM_DEFAULT_AGENT_ID,
205
+ ocrModel: "olm_ocr",
206
+ showScrollToTopButton: false,
207
+ showScrollToBottomButton: false,
208
+ createMenuOpenCount: 0,
209
+ };
210
+
211
+ // --- Slice 创建 ---
212
+ const createSliceWithThunks = buildCreateSlice({
213
+ creators: { asyncThunk: asyncThunkCreator },
214
+ });
215
+
216
+ const hasOwn = (target: object, key: string): boolean =>
217
+ Object.prototype.hasOwnProperty.call(target, key);
218
+
219
+ const normalizeSidebarVisibleTypesSetting = (
220
+ value: unknown
221
+ ): SidebarVisibleType[] => {
222
+ const rawValues = Array.isArray(value)
223
+ ? value.filter((item): item is string => typeof item === "string")
224
+ : undefined;
225
+ const normalized = normalizeSidebarVisibleTypes(rawValues);
226
+
227
+ if (
228
+ rawValues &&
229
+ areSidebarVisibleTypesEqual(normalized, LEGACY_DEFAULT_SIDEBAR_VISIBLE_TYPES)
230
+ ) {
231
+ return [...DEFAULT_SIDEBAR_VISIBLE_TYPES];
232
+ }
233
+
234
+ return normalized;
235
+ };
236
+
237
+ const normalizeDefaultAgentIdSetting = (
238
+ value: unknown
239
+ ): string | undefined => {
240
+ if (typeof value !== "string" || value.trim() === "") {
241
+ return undefined;
242
+ }
243
+
244
+ return value === legacyNoloAgentId ||
245
+ value === noloAgentId ||
246
+ value === SYSTEM_DEFAULT_AGENT_ID
247
+ ? SYSTEM_DEFAULT_AGENT_ID
248
+ : value;
249
+ };
250
+
251
+ const normalizeAuthorityHomeServerSetting = (value: unknown): string | null => {
252
+ if (typeof value !== "string") return null;
253
+ const trimmed = value.trim().replace(/\/+$/, "");
254
+ return /^https?:\/\//i.test(trimmed) ? trimmed : null;
255
+ };
256
+
257
+ const normalizeTonePresetSetting = (value: unknown): TonePreset => {
258
+ switch (value) {
259
+ case "professional":
260
+ case "friendly":
261
+ case "direct":
262
+ case "pragmatic":
263
+ case "default":
264
+ return value;
265
+ default:
266
+ return DEFAULT_USER_PREFERENCE_PROFILE.tone?.preset ?? "default";
267
+ }
268
+ };
269
+
270
+ const normalizePolicyLevelSetting = (
271
+ value: unknown,
272
+ fallback: KnowledgeCaptureLevel | SpaceContextLevel
273
+ ): KnowledgeCaptureLevel | SpaceContextLevel => {
274
+ const n = Number(value);
275
+ if (n === 1 || n === 2 || n === 3 || n === 4) {
276
+ return n;
277
+ }
278
+ return fallback;
279
+ };
280
+
281
+ const resolveDefaultAgentIdSetting = (value: unknown): string =>
282
+ normalizeDefaultAgentIdSetting(value) ?? SYSTEM_DEFAULT_AGENT_ID;
283
+
284
+ const selectResolvedDefaultAgentId = (value: unknown): string => {
285
+ const normalizedValue = resolveDefaultAgentIdSetting(value);
286
+ return normalizedValue === SYSTEM_DEFAULT_AGENT_ID
287
+ ? noloAgentId
288
+ : normalizedValue;
289
+ };
290
+
291
+ const hexToRgbString = (value?: string): string | null => {
292
+ if (typeof value !== "string") return null;
293
+ const normalized = value.trim().replace(/^#/, "");
294
+ const safe = normalized.length === 3
295
+ ? normalized
296
+ .split("")
297
+ .map((char) => char + char)
298
+ .join("")
299
+ : normalized;
300
+
301
+ if (!/^[0-9a-fA-F]{6}$/.test(safe)) return null;
302
+
303
+ const intValue = Number.parseInt(safe, 16);
304
+ const r = (intValue >> 16) & 255;
305
+ const g = (intValue >> 8) & 255;
306
+ const b = intValue & 255;
307
+ return `${r}, ${g}, ${b}`;
308
+ };
309
+
310
+ const alphaColor = (hex: string | undefined, alpha: number, fallback: string): string => {
311
+ const rgb = hexToRgbString(hex);
312
+ return rgb ? `rgba(${rgb}, ${alpha})` : fallback;
313
+ };
314
+
315
+ const getCachedDefaultAgentRegisterRecord = (
316
+ getState: () => RootState,
317
+ registerKey: string
318
+ ) => {
319
+ const record = getState().db.entities[registerKey];
320
+ return record && typeof record === "object" ? record : null;
321
+ };
322
+
323
+ const persistDefaultAgentRegister = async (
324
+ dispatch: any,
325
+ getState: () => RootState,
326
+ userId: string,
327
+ value: string | null,
328
+ ) => {
329
+ const registerKey = createUserPreferenceKey.defaultAgent(userId);
330
+ let previousRecord = getCachedDefaultAgentRegisterRecord(getState, registerKey);
331
+ if (!previousRecord) {
332
+ previousRecord = await dispatch(getSettingDbActionThunks().readAndWait(registerKey))
333
+ .unwrap()
334
+ .catch(() => null);
335
+ }
336
+
337
+ const previousValue =
338
+ readUserPreferenceRegisterValue<string>(
339
+ previousRecord,
340
+ USER_PREFERENCE_NAMES.DEFAULT_AGENT
341
+ ) ?? null;
342
+ if (previousRecord && previousValue === value) {
343
+ return;
344
+ }
345
+
346
+ await dispatch(
347
+ getSettingDbActionThunks().write({
348
+ customKey: registerKey,
349
+ data: buildDefaultAgentPreferenceRegisterRecord({
350
+ userId,
351
+ defaultAgentId: value,
352
+ previousRecord,
353
+ }),
354
+ })
355
+ ).unwrap();
356
+ };
357
+
358
+ const omitKeys = <T extends Record<string, any>>(
359
+ record: T,
360
+ keys: readonly string[],
361
+ ) => {
362
+ const next = { ...record };
363
+ keys.forEach((key) => {
364
+ delete next[key];
365
+ });
366
+ return next;
367
+ };
368
+
369
+ const normalizeSettingChanges = (
370
+ changes: Partial<SettingState>
371
+ ): Partial<SettingState> => {
372
+ let normalizedChanges = changes;
373
+
374
+ if (hasOwn(normalizedChanges, "sidebarVisibleTypes")) {
375
+ normalizedChanges = {
376
+ ...normalizedChanges,
377
+ sidebarVisibleTypes: normalizeSidebarVisibleTypesSetting(
378
+ normalizedChanges.sidebarVisibleTypes
379
+ ),
380
+ };
381
+ }
382
+
383
+ if (hasOwn(normalizedChanges, "defaultAgentId")) {
384
+ normalizedChanges = {
385
+ ...normalizedChanges,
386
+ defaultAgentId: normalizeDefaultAgentIdSetting(
387
+ normalizedChanges.defaultAgentId
388
+ ),
389
+ };
390
+ }
391
+
392
+ if (hasOwn(normalizedChanges, "defaultSpaceId")) {
393
+ normalizedChanges = {
394
+ ...normalizedChanges,
395
+ defaultSpaceId: normalizeDefaultSpaceIdPreference(
396
+ normalizedChanges.defaultSpaceId
397
+ ),
398
+ };
399
+ }
400
+
401
+ if (hasOwn(normalizedChanges, "themeName")) {
402
+ const themeName = normalizeThemeName(normalizedChanges.themeName);
403
+ normalizedChanges = themeName
404
+ ? {
405
+ ...normalizedChanges,
406
+ themeName,
407
+ }
408
+ : omitKeys(normalizedChanges, ["themeName"]);
409
+ }
410
+
411
+ if (hasOwn(normalizedChanges, "fontPreset")) {
412
+ const fontPreset = normalizeFontPreset(normalizedChanges.fontPreset);
413
+ normalizedChanges = fontPreset
414
+ ? {
415
+ ...normalizedChanges,
416
+ fontPreset,
417
+ }
418
+ : omitKeys(normalizedChanges, ["fontPreset"]);
419
+ }
420
+
421
+ if (hasOwn(normalizedChanges, "userTonePreset")) {
422
+ normalizedChanges = {
423
+ ...normalizedChanges,
424
+ userTonePreset: normalizeTonePresetSetting(
425
+ normalizedChanges.userTonePreset
426
+ ),
427
+ };
428
+ }
429
+
430
+ if (hasOwn(normalizedChanges, "knowledgeCaptureLevel")) {
431
+ normalizedChanges = {
432
+ ...normalizedChanges,
433
+ knowledgeCaptureLevel: normalizePolicyLevelSetting(
434
+ normalizedChanges.knowledgeCaptureLevel,
435
+ DEFAULT_USER_PREFERENCE_PROFILE.knowledgeCaptureLevel
436
+ ) as KnowledgeCaptureLevel,
437
+ };
438
+ }
439
+
440
+ if (hasOwn(normalizedChanges, "spaceContextLevel")) {
441
+ normalizedChanges = {
442
+ ...normalizedChanges,
443
+ spaceContextLevel: normalizePolicyLevelSetting(
444
+ normalizedChanges.spaceContextLevel,
445
+ DEFAULT_USER_PREFERENCE_PROFILE.spaceContextLevel
446
+ ) as SpaceContextLevel,
447
+ };
448
+ }
449
+
450
+ if (hasOwn(normalizedChanges, "autoApproveSelfUpdateFields")) {
451
+ normalizedChanges = {
452
+ ...normalizedChanges,
453
+ autoApproveSelfUpdateFields: normalizeAgentUpdateFieldList(
454
+ normalizedChanges.autoApproveSelfUpdateFields
455
+ ),
456
+ };
457
+ }
458
+
459
+ return normalizedChanges;
460
+ };
461
+
462
+ const LOCAL_FIRST_APPEARANCE_KEYS: ReadonlySet<keyof SettingState> = new Set([
463
+ "themeName",
464
+ "themeMode",
465
+ "isDark",
466
+ "density",
467
+ "fontPreset",
468
+ ]);
469
+
470
+ const LOCAL_ONLY_SETTINGS_KEYS: ReadonlySet<keyof SettingState> = new Set([
471
+ "themeName",
472
+ "themeMode",
473
+ "isDark",
474
+ "density",
475
+ "fontPreset",
476
+ ]);
477
+
478
+ const isLocalFirstAppearanceChange = (
479
+ changes: Partial<SettingState>
480
+ ): boolean => {
481
+ const keys = Object.keys(changes) as Array<keyof SettingState>;
482
+ return (
483
+ keys.length > 0 &&
484
+ keys.every((key) => LOCAL_FIRST_APPEARANCE_KEYS.has(key))
485
+ );
486
+ };
487
+
488
+ const stripRegisterBackedFieldsFromSettingsWrite = (
489
+ changes: Partial<SettingState>
490
+ ): Partial<SettingState> => {
491
+ return omitKeys(changes, [
492
+ "defaultSpaceId",
493
+ "userAuthorityRegistry",
494
+ ...LOCAL_ONLY_SETTINGS_KEYS,
495
+ ]);
496
+ };
497
+
498
+ const sanitizeStoredSettingsRecord = (
499
+ settingsRecord: any
500
+ ): Record<string, any> | null => {
501
+ if (!settingsRecord || typeof settingsRecord !== "object") {
502
+ return null;
503
+ }
504
+
505
+ const sanitizedSettings = omitKeys(settingsRecord, [
506
+ "defaultSpaceId",
507
+ "schemaVersion",
508
+ ...LOCAL_ONLY_SETTINGS_KEYS,
509
+ ]);
510
+ return Object.keys(sanitizedSettings).length > 0 ? sanitizedSettings : null;
511
+ };
512
+
513
+ export const hydrateStoredSettings = ({
514
+ userId,
515
+ settingsRecord,
516
+ defaultSpaceId,
517
+ authorityHomeServer,
518
+ }: {
519
+ userId?: string | null;
520
+ settingsRecord: any;
521
+ defaultSpaceId?: string | null;
522
+ authorityHomeServer?: string | null;
523
+ }): Record<string, any> | null => {
524
+ const resolvedDefaultAgentId =
525
+ normalizeDefaultAgentIdSetting(settingsRecord?.defaultAgentId) ??
526
+ SYSTEM_DEFAULT_AGENT_ID;
527
+
528
+ const baseSettings = sanitizeStoredSettingsRecord(settingsRecord) ?? {};
529
+ const normalizedAuthorityHome =
530
+ normalizeAuthorityHomeServerSetting(authorityHomeServer);
531
+ const userAuthorityRegistry =
532
+ userId && normalizedAuthorityHome
533
+ ? {
534
+ ...((baseSettings.userAuthorityRegistry &&
535
+ typeof baseSettings.userAuthorityRegistry === "object")
536
+ ? baseSettings.userAuthorityRegistry
537
+ : {}),
538
+ [userId]: normalizedAuthorityHome,
539
+ }
540
+ : baseSettings.userAuthorityRegistry;
541
+ const hydratedSettings = {
542
+ ...baseSettings,
543
+ ...(userAuthorityRegistry ? { userAuthorityRegistry } : {}),
544
+ defaultSpaceId: normalizeDefaultSpaceIdPreference(defaultSpaceId) ?? null,
545
+ defaultAgentId: resolvedDefaultAgentId,
546
+ };
547
+
548
+ return Object.keys(hydratedSettings).length > 0 ? hydratedSettings : null;
549
+ };
550
+
551
+ export const buildSettingsPersistencePlan = ({
552
+ userId,
553
+ currentSettings,
554
+ changes,
555
+ previousDefaultAgentRecord,
556
+ }: {
557
+ userId: string;
558
+ currentSettings: Partial<SettingState>;
559
+ changes: Partial<SettingState>;
560
+ previousDefaultAgentRecord: any;
561
+ }) => {
562
+ const normalizedChanges = normalizeSettingChanges(changes);
563
+ const persistedChanges =
564
+ stripRegisterBackedFieldsFromSettingsWrite(normalizedChanges);
565
+ const previousDefaultAgentId =
566
+ normalizeDefaultAgentIdSetting(currentSettings.defaultAgentId) ??
567
+ SYSTEM_DEFAULT_AGENT_ID;
568
+ const nextDefaultAgentId =
569
+ normalizeDefaultAgentIdSetting(normalizedChanges.defaultAgentId) ?? null;
570
+
571
+ const previousDefaultAgentValue =
572
+ readUserPreferenceRegisterValue<string>(
573
+ previousDefaultAgentRecord,
574
+ USER_PREFERENCE_NAMES.DEFAULT_AGENT
575
+ ) ?? null;
576
+
577
+ const defaultAgentRegisterWrite =
578
+ hasOwn(normalizedChanges, "defaultAgentId") &&
579
+ previousDefaultAgentId !== (nextDefaultAgentId ?? SYSTEM_DEFAULT_AGENT_ID) &&
580
+ (!previousDefaultAgentRecord || previousDefaultAgentValue !== nextDefaultAgentId)
581
+ ? {
582
+ customKey: createUserPreferenceKey.defaultAgent(userId),
583
+ data: buildDefaultAgentPreferenceRegisterRecord({
584
+ userId,
585
+ defaultAgentId: nextDefaultAgentId,
586
+ previousRecord: previousDefaultAgentRecord,
587
+ }),
588
+ }
589
+ : null;
590
+
591
+ const settingsPatch =
592
+ Object.keys(persistedChanges).length > 0
593
+ ? {
594
+ dbKey: createUserKey.settings(userId),
595
+ changes: withSettingsRecordSchema(persistedChanges),
596
+ }
597
+ : null;
598
+
599
+ return {
600
+ normalizedChanges,
601
+ defaultAgentRegisterWrite,
602
+ settingsPatch,
603
+ };
604
+ };
605
+
606
+ const settingSlice = createSliceWithThunks({
607
+ name: "settings",
608
+ initialState,
609
+ reducers: (create) => ({
610
+ _updateSettingsState: (
611
+ state,
612
+ action: PayloadAction<Partial<SettingState>>
613
+ ) => {
614
+ Object.assign(state, normalizeSettingChanges(action.payload));
615
+ },
616
+ clearDefaultSpaceId: (state) => {
617
+ state.defaultSpaceId = initialState.defaultSpaceId;
618
+ },
619
+ addHostToCurrentServer: (state, action: PayloadAction<string>) => {
620
+ const rawValue = action.payload;
621
+ if (typeof rawValue !== "string" || rawValue.trim() === "") return;
622
+
623
+ const trimmed = rawValue.trim();
624
+ if (/^https?:\/\//i.test(trimmed)) {
625
+ try {
626
+ state.currentServer = new URL(trimmed).origin;
627
+ return;
628
+ } catch {
629
+ return;
630
+ }
631
+ }
632
+
633
+ const host = trimmed.replace(/^\/+|\/+$/g, "");
634
+ const [hostname] = host.split(":");
635
+ if (!hostname) return;
636
+ const isIpAddress = /^(\d{1,3}\.){3}\d{1,3}$/.test(hostname);
637
+ const isLocal =
638
+ ["nolotus.local", "localhost"].includes(hostname) || isIpAddress;
639
+ const protocol = isLocal ? "http" : "https";
640
+ state.currentServer = `${protocol}://${host}`;
641
+ },
642
+ getSettings: create.asyncThunk(
643
+ async (_, { dispatch, getState }) => {
644
+ const userId = selectUserId(getState() as RootState);
645
+ if (!userId) return null;
646
+ const settingsKey = createUserKey.settings(userId);
647
+ const authorityHomeKey = createUserPreferenceKey.authorityHome(userId);
648
+ const [settingsRecord, defaultSpaceId, authorityHomeRecord] = await Promise.all([
649
+ dispatch(getSettingDbActionThunks().readAndWait(settingsKey))
650
+ .unwrap()
651
+ .catch(() => null),
652
+ readDefaultSpaceIdPreference(dispatch, userId),
653
+ dispatch(getSettingDbActionThunks().readAndWait(authorityHomeKey))
654
+ .unwrap()
655
+ .catch(() => null),
656
+ ]);
657
+ const authorityHomeServer =
658
+ readUserPreferenceRegisterValue<string>(
659
+ authorityHomeRecord,
660
+ USER_PREFERENCE_NAMES.AUTHORITY_HOME
661
+ ) ?? null;
662
+
663
+ return hydrateStoredSettings({
664
+ userId,
665
+ settingsRecord,
666
+ defaultSpaceId,
667
+ authorityHomeServer,
668
+ });
669
+ },
670
+ {
671
+ fulfilled: (state, action) => {
672
+ if (action.payload) {
673
+ const normalizedPayload = normalizeSettingChanges(
674
+ action.payload as Partial<SettingState>
675
+ );
676
+ const { currentServer: _ignored, ...settingsToApply } = normalizedPayload;
677
+ Object.assign(state, settingsToApply);
678
+ }
679
+ },
680
+ }
681
+ ),
682
+ setSettings: create.asyncThunk(
683
+ async (changes: Partial<SettingState>, { dispatch, getState }) => {
684
+ const currentSettings = (getState() as RootState).settings;
685
+ const normalizedChanges = normalizeSettingChanges(changes);
686
+ const nextDefaultSpaceId =
687
+ normalizeDefaultSpaceIdPreference(normalizedChanges.defaultSpaceId) ??
688
+ null;
689
+ const previousDefaultAgentId =
690
+ normalizeDefaultAgentIdSetting(
691
+ currentSettings.defaultAgentId,
692
+ ) ?? SYSTEM_DEFAULT_AGENT_ID;
693
+ const userId = selectUserId(getState() as RootState);
694
+ if (!userId) {
695
+ if (!isLocalFirstAppearanceChange(normalizedChanges)) {
696
+ throw new Error("User not found for persisting settings.");
697
+ }
698
+
699
+ dispatch(settingSlice.actions._updateSettingsState(normalizedChanges));
700
+ return normalizedChanges;
701
+ }
702
+ const persistencePlan = buildSettingsPersistencePlan({
703
+ userId,
704
+ currentSettings,
705
+ changes: normalizedChanges,
706
+ previousDefaultAgentRecord: null,
707
+ });
708
+ dispatch(settingSlice.actions._updateSettingsState(persistencePlan.normalizedChanges));
709
+ if (hasOwn(changes, "defaultSpaceId")) {
710
+ await persistDefaultSpacePreference(
711
+ dispatch,
712
+ userId,
713
+ nextDefaultSpaceId
714
+ );
715
+ }
716
+ const nextDefaultAgentId =
717
+ normalizeDefaultAgentIdSetting(persistencePlan.normalizedChanges.defaultAgentId) ??
718
+ null;
719
+ if (
720
+ hasOwn(changes, "defaultAgentId") &&
721
+ previousDefaultAgentId !== (nextDefaultAgentId ?? SYSTEM_DEFAULT_AGENT_ID)
722
+ ) {
723
+ await persistDefaultAgentRegister(
724
+ dispatch,
725
+ () => getState() as RootState,
726
+ userId,
727
+ nextDefaultAgentId,
728
+ );
729
+ }
730
+ if (persistencePlan.settingsPatch) {
731
+ await dispatch(
732
+ getSettingDbActionThunks().upsert({
733
+ dbKey: persistencePlan.settingsPatch.dbKey,
734
+ data: persistencePlan.settingsPatch.changes,
735
+ })
736
+ ).unwrap();
737
+ }
738
+ return persistencePlan.normalizedChanges;
739
+ }
740
+ ),
741
+ changeTheme: create.asyncThunk(
742
+ async (themeName: keyof typeof THEME_COLORS, { dispatch }) =>
743
+ dispatch(setSettings({ themeName })).unwrap()
744
+ ),
745
+ changeDensity: create.asyncThunk(
746
+ async (density: "compact" | "spacious", { dispatch }) =>
747
+ dispatch(setSettings({ density })).unwrap()
748
+ ),
749
+ changeFontPreset: create.asyncThunk(
750
+ async (fontPreset: FontPreset, { dispatch }) =>
751
+ dispatch(setSettings({ fontPreset })).unwrap()
752
+ ),
753
+ changeDarkMode: create.asyncThunk(async (isDark: boolean, { dispatch }) =>
754
+ dispatch(setSettings({ isDark, themeMode: isDark ? "dark" : "light" })).unwrap()
755
+ ),
756
+ toggleShowThinking: create.asyncThunk(async (_, { dispatch, getState }) => {
757
+ const currentShowThinking = (getState() as RootState).settings
758
+ .showThinking;
759
+ return dispatch(
760
+ setSettings({ showThinking: !currentShowThinking })
761
+ ).unwrap();
762
+ }),
763
+ setThemeFollowsSystem: create.asyncThunk(
764
+ async (follows: boolean, { dispatch }) =>
765
+ dispatch(setSettings({ themeMode: follows ? "system" : "light" })).unwrap()
766
+ ),
767
+ setSidebarWidth: create.asyncThunk(
768
+ async (sidebarWidth: number, { dispatch }) =>
769
+ dispatch(setSettings({ sidebarWidth })).unwrap()
770
+ ),
771
+ toggleEnableReadCurrentSpace: create.asyncThunk(
772
+ async (_, { dispatch, getState }) => {
773
+ const current = (getState() as RootState).settings
774
+ .enableReadCurrentSpace;
775
+ return dispatch(
776
+ setSettings({
777
+ enableReadCurrentSpace: !current,
778
+ spaceContextLevel: current ? 1 : DEFAULT_USER_PREFERENCE_PROFILE.spaceContextLevel,
779
+ })
780
+ ).unwrap();
781
+ }
782
+ ),
783
+ setEditorDefaultMode: create.asyncThunk(
784
+ async (mode: "markdown" | "block", { dispatch }) =>
785
+ dispatch(setSettings({ editorDefaultMode: mode })).unwrap()
786
+ ),
787
+ setEditorLightCodeTheme: create.asyncThunk(
788
+ async (theme: string, { dispatch }) =>
789
+ dispatch(setSettings({ editorLightCodeTheme: theme })).unwrap()
790
+ ),
791
+ setEditorDarkCodeTheme: create.asyncThunk(
792
+ async (theme: string, { dispatch }) =>
793
+ dispatch(setSettings({ editorDarkCodeTheme: theme })).unwrap()
794
+ ),
795
+ setEditorCodeTheme: create.asyncThunk(async (theme: string, { dispatch }) =>
796
+ dispatch(
797
+ setSettings({
798
+ editorLightCodeTheme: theme,
799
+ editorDarkCodeTheme: theme,
800
+ })
801
+ ).unwrap()
802
+ ),
803
+ toggleEditorWordCount: create.asyncThunk(
804
+ async (_, { dispatch, getState }) => {
805
+ const current = (getState() as RootState).settings
806
+ .editorWordCountEnabled;
807
+ return dispatch(
808
+ setSettings({ editorWordCountEnabled: !current })
809
+ ).unwrap();
810
+ }
811
+ ),
812
+ toggleEditorShortcut: create.asyncThunk(
813
+ async (key: string, { dispatch, getState }) => {
814
+ const currentShortcuts = (getState() as RootState).settings
815
+ .editorShortcuts;
816
+ const newShortcuts = {
817
+ ...currentShortcuts,
818
+ [key]: !currentShortcuts[key],
819
+ };
820
+ return dispatch(
821
+ setSettings({ editorShortcuts: newShortcuts })
822
+ ).unwrap();
823
+ }
824
+ ),
825
+ setEditorFontSize: create.asyncThunk(
826
+ async (fontSize: number, { dispatch }) =>
827
+ dispatch(setSettings({ editorFontSize: fontSize })).unwrap()
828
+ ),
829
+ toggleEditorAutoSave: create.asyncThunk(
830
+ async (_, { dispatch, getState }) => {
831
+ const current = (getState() as RootState).settings.editorAutoSave;
832
+ return dispatch(setSettings({ editorAutoSave: !current })).unwrap();
833
+ }
834
+ ),
835
+ setEditorAutoSaveInterval: create.asyncThunk(
836
+ async (interval: number, { dispatch }) =>
837
+ dispatch(setSettings({ editorAutoSaveInterval: interval })).unwrap()
838
+ ),
839
+ setGlobalPrompt: create.asyncThunk(
840
+ async (prompt: string, { dispatch }) =>
841
+ dispatch(setSettings({ globalPrompt: prompt })).unwrap()
842
+ ),
843
+ setUserTonePreset: create.asyncThunk(
844
+ async (tone: TonePreset, { dispatch }) =>
845
+ dispatch(setSettings({ userTonePreset: tone })).unwrap()
846
+ ),
847
+ setKnowledgeCaptureLevel: create.asyncThunk(
848
+ async (level: KnowledgeCaptureLevel, { dispatch }) =>
849
+ dispatch(setSettings({ knowledgeCaptureLevel: level })).unwrap()
850
+ ),
851
+ setSpaceContextLevel: create.asyncThunk(
852
+ async (level: SpaceContextLevel, { dispatch }) =>
853
+ dispatch(
854
+ setSettings({
855
+ spaceContextLevel: level,
856
+ enableReadCurrentSpace: level > 1,
857
+ })
858
+ ).unwrap()
859
+ ),
860
+ // ADDED action
861
+ setAiRecentContentLimit: create.asyncThunk(
862
+ async (limit: number, { dispatch }) =>
863
+ dispatch(setSettings({ aiRecentContentLimit: limit })).unwrap()
864
+ ),
865
+ setContextRetention: create.asyncThunk(
866
+ async (retention: number, { dispatch }) =>
867
+ dispatch(setSettings({ contextRetention: retention })).unwrap()
868
+ ),
869
+ setMaxExecutionTime: create.asyncThunk(
870
+ async (time: number, { dispatch }) =>
871
+ dispatch(setSettings({ maxExecutionTime: time })).unwrap()
872
+ ),
873
+ setDefaultAgentId: create.asyncThunk(
874
+ async (agentId: string, { dispatch }) =>
875
+ dispatch(setSettings({ defaultAgentId: agentId })).unwrap()
876
+ ),
877
+ setPreferredAnimationSet: create.asyncThunk(
878
+ async (index: number, { dispatch }) =>
879
+ dispatch(setSettings({ preferredAnimationSet: index })).unwrap()
880
+ ),
881
+ setThemeMode: create.asyncThunk(
882
+ async (mode: "system" | "light" | "dark", { dispatch }) => {
883
+ const changes: Partial<SettingState> = { themeMode: mode };
884
+ const systemPrefersDark =
885
+ typeof window !== "undefined" &&
886
+ window.matchMedia(SYSTEM_DARK_MEDIA_QUERY).matches;
887
+ changes.isDark = resolveThemeModeIsDark(mode, systemPrefersDark);
888
+ return dispatch(setSettings(changes)).unwrap();
889
+ }
890
+ ),
891
+ }),
892
+ });
893
+
894
+ // --- 导出 Actions ---
895
+ export const {
896
+ getSettings,
897
+ setSettings,
898
+ clearDefaultSpaceId,
899
+ addHostToCurrentServer,
900
+ changeTheme,
901
+ changeDensity,
902
+ changeFontPreset,
903
+ changeDarkMode,
904
+ toggleShowThinking,
905
+ setThemeFollowsSystem,
906
+ setSidebarWidth,
907
+ toggleEnableReadCurrentSpace,
908
+ setEditorDefaultMode,
909
+ setEditorLightCodeTheme,
910
+ setEditorDarkCodeTheme,
911
+ setEditorCodeTheme,
912
+ toggleEditorWordCount,
913
+ toggleEditorShortcut,
914
+ setEditorFontSize,
915
+ toggleEditorAutoSave,
916
+ setEditorAutoSaveInterval,
917
+ setGlobalPrompt,
918
+ setUserTonePreset,
919
+ setKnowledgeCaptureLevel,
920
+ setSpaceContextLevel,
921
+ setAiRecentContentLimit,
922
+ setContextRetention, // ADDED
923
+ setMaxExecutionTime,
924
+ setDefaultAgentId,
925
+ setPreferredAnimationSet,
926
+ setThemeMode,
927
+ } = settingSlice.actions;
928
+
929
+ // --- 导出 Selectors ---
930
+ export const selectSettings = (state: RootState) => state.settings;
931
+ const isLocalServerUrl = (value: string | undefined): boolean => {
932
+ if (typeof value !== "string") return false;
933
+ return /^https?:\/\/(?:(?:\d{1,3}\.){3}\d{1,3}|localhost|nolotus\.local)(?::\d+)?$/i.test(
934
+ value.trim()
935
+ );
936
+ };
937
+
938
+ const resolveDesktopSafeServer = (value: string | undefined): string => {
939
+ if (!getIsDesktopApp()) return value || SERVERS.MAIN;
940
+ return isLocalServerUrl(value) ? SERVERS.MAIN : value || SERVERS.MAIN;
941
+ };
942
+
943
+ export const selectCurrentServer = createSelector(
944
+ [selectSettings],
945
+ (settings) => resolveDesktopSafeServer(settings.currentServer)
946
+ );
947
+ export const selectSyncServers = createSelector([selectSettings], (settings): string[] =>
948
+ (settings.syncServers || []).filter(
949
+ (server) => !getIsDesktopApp() || !isLocalServerUrl(server)
950
+ )
951
+ );
952
+ export const selectRemoteServer = selectCurrentServer;
953
+ export const selectRemoteSyncServers = selectSyncServers;
954
+ export const selectRemoteServers = createSelector(
955
+ [selectRemoteServer, selectRemoteSyncServers],
956
+ (currentServer, syncServers) => getAllServers(currentServer, syncServers)
957
+ );
958
+ export const selectDefaultSpaceId = (
959
+ state: RootState
960
+ ): string | null | undefined => state.settings.defaultSpaceId;
961
+
962
+ export const selectPreferredAnimationSet = (state: RootState): number =>
963
+ state.settings.preferredAnimationSet ?? DEFAULT_ANIMATION_SET_INDEX;
964
+ export const selectShowThinking = (state: RootState): boolean =>
965
+ state.settings.showThinking;
966
+ export const selectMaxCost = (state: RootState): number =>
967
+ state.settings.maxCost;
968
+ export const selectMaxExecutionTime = (state: RootState): number =>
969
+ state.settings.maxExecutionTime;
970
+ export const selectIsDark = (state: RootState): boolean =>
971
+ state.settings.isDark;
972
+ export const selectThemeMode = (state: RootState): "system" | "light" | "dark" =>
973
+ state.settings.themeMode ?? "system";
974
+ export const selectHeaderHeight = (state: RootState): number =>
975
+ state.settings.headerHeight;
976
+ export const selectThemeName = (state: RootState): keyof typeof THEME_COLORS =>
977
+ state.settings.themeName;
978
+ export const selectThemeFollowsSystem = (state: RootState): boolean =>
979
+ (state.settings.themeMode ?? "system") === "system";
980
+ export const selectSidebarWidth = (state: RootState): number =>
981
+ state.settings.sidebarWidth;
982
+ export const selectDensity = (state: RootState): "compact" | "spacious" =>
983
+ state.settings.density ?? "compact";
984
+ export const selectFontPreset = (state: RootState): FontPreset =>
985
+ normalizeFontPreset(state.settings.fontPreset) ?? DEFAULT_FONT_PRESET;
986
+ export const selectEnableReadCurrentSpace = (state: RootState): boolean =>
987
+ state.settings.enableReadCurrentSpace;
988
+ export const selectSidebarVisibleTypes = createSelector(
989
+ [(state: RootState) => state.settings.sidebarVisibleTypes],
990
+ (sidebarVisibleTypes): SidebarVisibleType[] =>
991
+ normalizeSidebarVisibleTypesSetting(sidebarVisibleTypes)
992
+ );
993
+ export const selectGlobalPrompt = (state: RootState): string =>
994
+ state.settings.globalPrompt;
995
+ export const selectUserTonePreset = (state: RootState): TonePreset =>
996
+ normalizeTonePresetSetting(state.settings.userTonePreset);
997
+ export const selectKnowledgeCaptureLevel = (
998
+ state: RootState
999
+ ): KnowledgeCaptureLevel =>
1000
+ normalizePolicyLevelSetting(
1001
+ state.settings.knowledgeCaptureLevel,
1002
+ DEFAULT_USER_PREFERENCE_PROFILE.knowledgeCaptureLevel
1003
+ ) as KnowledgeCaptureLevel;
1004
+ export const selectSpaceContextLevel = (
1005
+ state: RootState
1006
+ ): SpaceContextLevel =>
1007
+ state.settings.enableReadCurrentSpace === false
1008
+ ? 1
1009
+ :
1010
+ normalizePolicyLevelSetting(
1011
+ state.settings.spaceContextLevel,
1012
+ DEFAULT_USER_PREFERENCE_PROFILE.spaceContextLevel
1013
+ ) as SpaceContextLevel;
1014
+ export const selectAutoApproveSelfUpdateFields = createSelector(
1015
+ [(state: RootState) => state.settings.autoApproveSelfUpdateFields],
1016
+ (fields) =>
1017
+ normalizeAgentUpdateFieldList(
1018
+ fields,
1019
+ DEFAULT_AUTO_APPROVED_SELF_UPDATE_FIELDS
1020
+ )
1021
+ );
1022
+ export const selectAiRecentContentLimit = (state: RootState): number =>
1023
+ state.settings.aiRecentContentLimit ?? 50; // ADDED
1024
+ export const selectContextRetention = (state: RootState): number =>
1025
+ state.settings.contextRetention ?? 50;
1026
+ export const selectDefaultAgentPreference = (state: RootState): string =>
1027
+ resolveDefaultAgentIdSetting(state.settings.defaultAgentId);
1028
+ export const selectDefaultAgentId = (state: RootState): string =>
1029
+ selectResolvedDefaultAgentId(state.settings.defaultAgentId);
1030
+
1031
+ export const selectOcrModel = (
1032
+ state: RootState
1033
+ ): "none" | "olm_ocr" =>
1034
+ state.settings.ocrModel === "none" ? "none" : "olm_ocr";
1035
+
1036
+ export const selectShowScrollToTopButton = (state: RootState): boolean =>
1037
+ state.settings.showScrollToTopButton ?? false;
1038
+ export const selectShowScrollToBottomButton = (state: RootState): boolean =>
1039
+ state.settings.showScrollToBottomButton ?? false;
1040
+ export const selectCreateMenuOpenCount = (state: RootState): number => {
1041
+ const value = Number(state.settings.createMenuOpenCount);
1042
+ return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
1043
+ };
1044
+
1045
+
1046
+ export const selectEditorDefaultMode = (
1047
+ state: RootState
1048
+ ): "markdown" | "block" => state.settings.editorDefaultMode;
1049
+ export const selectEditorLightCodeTheme = (state: RootState): string =>
1050
+ state.settings.editorLightCodeTheme;
1051
+ export const selectEditorDarkCodeTheme = (state: RootState): string =>
1052
+ state.settings.editorDarkCodeTheme;
1053
+ export const selectEditorCodeTheme = createSelector(
1054
+ [selectEditorLightCodeTheme, selectEditorDarkCodeTheme, selectIsDark],
1055
+ (lightTheme, darkTheme, isDark) => (isDark ? darkTheme : lightTheme)
1056
+ );
1057
+ export const selectEditorWordCountEnabled = (state: RootState): boolean =>
1058
+ state.settings.editorWordCountEnabled;
1059
+ export const selectEditorShortcuts = (state: RootState) =>
1060
+ state.settings.editorShortcuts;
1061
+ export const selectEditorFontSize = (state: RootState): number =>
1062
+ state.settings.editorFontSize;
1063
+ export const selectEditorAutoSave = (state: RootState): boolean =>
1064
+ state.settings.editorAutoSave;
1065
+ export const selectEditorAutoSaveInterval = (state: RootState): number =>
1066
+ state.settings.editorAutoSaveInterval;
1067
+
1068
+ export const selectTheme = createSelector(
1069
+ [
1070
+ selectThemeName,
1071
+ selectIsDark,
1072
+ selectSidebarWidth,
1073
+ selectHeaderHeight,
1074
+ selectDensity,
1075
+ selectFontPreset,
1076
+ ],
1077
+ (themeName, isDark, sidebarWidth, headerHeight, density, fontPreset) => {
1078
+ const mode = isDark ? "dark" : "light";
1079
+ const validThemeName = THEME_COLORS[themeName]
1080
+ ? themeName
1081
+ : DEFAULT_THEME_NAME;
1082
+ const validFontPreset =
1083
+ normalizeFontPreset(fontPreset) ?? DEFAULT_FONT_PRESET;
1084
+ const baseColors = MODE_COLORS[mode];
1085
+ const themeColors = THEME_COLORS[validThemeName][mode];
1086
+ // 主题 surface 字段覆盖中性底色,实现完整主题体验
1087
+ const c = { ...baseColors, ...themeColors } as typeof baseColors & typeof themeColors & { textHeading?: string };
1088
+ const compact = density === "compact";
1089
+ return {
1090
+ sidebarWidth: `${sidebarWidth}px`,
1091
+ headerHeight: `${headerHeight}px`,
1092
+ sidebarItemHeight: compact ? "36px" : "40px",
1093
+ sidebarIconSize: compact ? "18px" : "20px",
1094
+ sidebarItemGap: compact ? "2px" : "4px",
1095
+ // Density-aware spacing scale (4px grid → 5px grid)
1096
+ space: compact
1097
+ ? SPACE
1098
+ : {
1099
+ 0: "0", 1: "5px", 2: "10px", 3: "14px", 4: "20px", 5: "24px",
1100
+ 6: "30px", 7: "34px", 8: "40px", 10: "50px", 12: "60px",
1101
+ 14: "70px", 16: "80px", 20: "100px", 24: "120px",
1102
+ },
1103
+ // Semantic typography scale
1104
+ fontSize: {
1105
+ xs: compact ? "11px" : "12px",
1106
+ sm: compact ? "12px" : "13px",
1107
+ base: compact ? "14px" : "15px",
1108
+ md: compact ? "14px" : "15.5px",
1109
+ lg: compact ? "16px" : "17.5px",
1110
+ xl: compact ? "20px" : "22px",
1111
+ "2xl": compact ? "24px" : "26px",
1112
+ },
1113
+ // Semantic line-height scale
1114
+ leading: {
1115
+ tight: compact ? "1.3" : "1.4",
1116
+ normal: compact ? "1.45" : "1.6",
1117
+ relaxed: compact ? "1.6" : "1.75",
1118
+ },
1119
+ // Component height scale (buttons, inputs, list items)
1120
+ control: {
1121
+ xs: compact ? "24px" : "28px",
1122
+ sm: compact ? "28px" : "32px",
1123
+ md: compact ? "36px" : "40px",
1124
+ lg: compact ? "40px" : "46px",
1125
+ xl: compact ? "48px" : "56px",
1126
+ },
1127
+ // Border radius scale
1128
+ radius: {
1129
+ sm: compact ? "4px" : "6px",
1130
+ md: compact ? "8px" : "10px",
1131
+ lg: compact ? "12px" : "14px",
1132
+ xl: compact ? "16px" : "20px",
1133
+ },
1134
+ font: FONT_PRESET_CSS_VARIABLES[validFontPreset],
1135
+ ...c,
1136
+ borderSubtle: c.borderLight,
1137
+ borderFaint: alphaColor(c.border, isDark ? 0.22 : 0.35, c.borderLight),
1138
+ borderStrong: c.borderHover,
1139
+ surface: c.backgroundSecondary,
1140
+ surfaceElevated: c.backgroundTertiary,
1141
+ surfaceCanvas: c.background,
1142
+ surfaceSidebar: isDark ? c.backgroundSecondary : c.background,
1143
+ surfacePanel: c.backgroundSecondary,
1144
+ surfaceCard: c.messageBackground,
1145
+ surfaceRaised: isDark ? c.backgroundTertiary : c.backgroundSecondary,
1146
+ surfaceInset: isDark ? c.backgroundSecondary : alphaColor(c.borderHover, 0.05, c.codeBackground),
1147
+ surfaceCode: c.codeBackground,
1148
+ surfaceInteractive: c.backgroundTertiary,
1149
+ surfaceInteractiveHover: c.backgroundHover,
1150
+ textMuted: c.textSecondary,
1151
+ textSubtle: c.textTertiary,
1152
+ textHeading: c.textHeading ?? c.text,
1153
+ borderMuted: alphaColor(c.borderHover, isDark ? 0.3 : 0.24, c.borderLight),
1154
+ accentSoft: alphaColor(c.primary, isDark ? 0.18 : 0.1, c.primaryGhost),
1155
+ shadow: c.shadowMedium,
1156
+ shadow1: `0 1px 2px ${c.shadowLight}`,
1157
+ shadow2: `0 8px 24px -12px ${c.shadowMedium}`,
1158
+ shadow3: `0 18px 44px -24px ${c.shadowHeavy}`,
1159
+ primaryBorder: c.borderAccent ?? c.primary,
1160
+ primaryBgStrong: alphaColor(c.primary, isDark ? 0.18 : 0.1, c.primaryGhost),
1161
+ success: c.success,
1162
+ warning: c.warning,
1163
+ info: c.info,
1164
+ successGhost: alphaColor(c.success, isDark ? 0.2 : 0.12, "rgba(16, 185, 129, 0.12)"),
1165
+ warningGhost: alphaColor(c.warning, isDark ? 0.2 : 0.12, "rgba(245, 158, 11, 0.12)"),
1166
+ infoGhost: alphaColor(c.info, isDark ? 0.2 : 0.12, "rgba(59, 130, 246, 0.12)"),
1167
+ errorGhost: alphaColor(c.error, isDark ? 0.2 : 0.12, "rgba(239, 68, 68, 0.12)"),
1168
+ primaryRgb: hexToRgbString(c.primary) ?? "59, 130, 246",
1169
+ focusRing: alphaColor(c.primary, isDark ? 0.3 : 0.22, c.primaryGhost),
1170
+ };
1171
+ }
1172
+ );
1173
+
1174
+ export const selectEditorConfig = createSelector(
1175
+ [
1176
+ selectEditorDefaultMode,
1177
+ selectEditorLightCodeTheme,
1178
+ selectEditorDarkCodeTheme,
1179
+ selectEditorWordCountEnabled,
1180
+ selectEditorShortcuts,
1181
+ selectEditorFontSize,
1182
+ selectEditorAutoSave,
1183
+ selectEditorAutoSaveInterval,
1184
+ selectIsDark,
1185
+ ],
1186
+ (
1187
+ defaultMode,
1188
+ lightCodeTheme,
1189
+ darkCodeTheme,
1190
+ wordCountEnabled,
1191
+ shortcuts,
1192
+ fontSize,
1193
+ autoSave,
1194
+ autoSaveInterval,
1195
+ isDark
1196
+ ) => {
1197
+ const codeTheme = isDark ? darkCodeTheme : lightCodeTheme;
1198
+ return {
1199
+ defaultMode,
1200
+ codeTheme,
1201
+ lightCodeTheme,
1202
+ darkCodeTheme,
1203
+ wordCountEnabled,
1204
+ shortcuts,
1205
+ fontSize,
1206
+ autoSave,
1207
+ autoSaveInterval,
1208
+ };
1209
+ }
1210
+ );
1211
+
1212
+ export default settingSlice.reducer;