nolo-cli 0.1.13 → 0.1.14

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 (320) hide show
  1. package/README.md +9 -2
  2. package/agent-runtime/hostAdapter.ts +53 -0
  3. package/agent-runtime/index.ts +28 -0
  4. package/agent-runtime/localLoop.ts +62 -0
  5. package/agent-runtime/runtimeDecision.ts +70 -0
  6. package/agent-runtime/types.ts +87 -0
  7. package/agentRuntimeCommands.ts +139 -22
  8. package/agentRuntimeLocal.ts +7 -0
  9. package/ai/agent/_executeModel.ts +118 -0
  10. package/ai/agent/agentSlice.ts +544 -1
  11. package/ai/agent/appWorkingMemory.ts +126 -0
  12. package/ai/agent/avatarUtils.ts +24 -0
  13. package/ai/agent/buildEditingContext.ts +373 -0
  14. package/ai/agent/buildSystemPrompt.ts +532 -0
  15. package/ai/agent/cleanAgentMessages.ts +140 -0
  16. package/ai/agent/cliChatClient.ts +119 -0
  17. package/ai/agent/contextCompiler.ts +107 -0
  18. package/ai/agent/contextLayerContract.ts +44 -0
  19. package/ai/agent/createAgentSchema.ts +234 -0
  20. package/ai/agent/executeToolCall.ts +58 -0
  21. package/ai/agent/fetchAgentContexts.ts +42 -0
  22. package/ai/agent/generatePrompt.ts +3 -0
  23. package/ai/agent/getFullChatContextKeys.ts +168 -0
  24. package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
  25. package/ai/agent/hooks/useAgentConfig.ts +61 -0
  26. package/ai/agent/hooks/useAgentDialog.ts +35 -0
  27. package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
  28. package/ai/agent/hooks/usePublicAgents.ts +473 -0
  29. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  30. package/ai/agent/planSlice.ts +259 -0
  31. package/ai/agent/referenceUtils.ts +229 -0
  32. package/ai/agent/runAgentBackground.ts +238 -0
  33. package/ai/agent/runAgentClientLoop.ts +138 -0
  34. package/ai/agent/runtimeGuidance.ts +97 -0
  35. package/ai/agent/runtimeServerBase.ts +37 -0
  36. package/ai/agent/server/fetchPublicAgents.ts +128 -0
  37. package/ai/agent/startParallelAgentStreams.ts +424 -0
  38. package/ai/agent/startupProtocol.ts +53 -0
  39. package/ai/agent/streamAgentChatTurn.ts +1299 -0
  40. package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
  41. package/ai/agent/types.ts +71 -0
  42. package/ai/agent/utils/imageOutput.ts +39 -0
  43. package/ai/agent/utils/publicImageAgentMode.ts +26 -0
  44. package/ai/agent/utils/sortUtils.ts +250 -0
  45. package/ai/agent/web/referencePickerUtils.ts +146 -0
  46. package/ai/ai.locale.ts +1083 -0
  47. package/ai/chat/accumulateToolCallChunks.ts +95 -0
  48. package/ai/chat/fetchUtils.native.ts +276 -0
  49. package/ai/chat/fetchUtils.ts +153 -0
  50. package/ai/chat/inlineImageUrlsForCustomProvider.ts +117 -0
  51. package/ai/chat/parseApiError.ts +64 -0
  52. package/ai/chat/parseMultilineSSE.ts +95 -0
  53. package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
  54. package/ai/chat/sendOpenAICompletionsRequest.ts +712 -0
  55. package/ai/chat/sendOpenAIResponseRequest.ts +512 -0
  56. package/ai/chat/shouldUseServerProxy.ts +18 -0
  57. package/ai/chat/sseClient.native.ts +91 -0
  58. package/ai/chat/sseClient.ts +67 -0
  59. package/ai/chat/streamReader.native.ts +31 -0
  60. package/ai/chat/streamReader.ts +62 -0
  61. package/ai/chat/updateTotalUsage.ts +72 -0
  62. package/ai/context/buildReferenceContext.ts +437 -0
  63. package/ai/context/calculateContextUsage.ts +133 -0
  64. package/ai/context/retention.ts +165 -0
  65. package/ai/context/tokenUtils.ts +78 -0
  66. package/ai/index.ts +1 -1
  67. package/ai/llm/agentCapabilities.ts +74 -0
  68. package/ai/llm/calculateGeminiImageTokens.ts +57 -0
  69. package/ai/llm/deepinfra.ts +28 -0
  70. package/ai/llm/fireworks.ts +68 -0
  71. package/ai/llm/generateRequestBody.ts +165 -0
  72. package/ai/llm/getModelContextWindow.ts +84 -0
  73. package/ai/llm/getNoloKey.ts +37 -0
  74. package/ai/llm/getPricing.ts +232 -0
  75. package/ai/llm/hooks/useModelPricing.ts +75 -0
  76. package/ai/llm/imagePricing.ts +66 -0
  77. package/ai/llm/isResponseAPIModel.ts +13 -0
  78. package/ai/llm/kimi.ts +18 -0
  79. package/ai/llm/mimo.ts +71 -0
  80. package/ai/llm/mistral.ts +22 -0
  81. package/ai/llm/modelAvatar.ts +427 -0
  82. package/ai/llm/models.ts +45 -0
  83. package/ai/llm/openrouterModels.ts +141 -0
  84. package/ai/llm/providers.ts +307 -0
  85. package/ai/llm/reasoningModels.ts +28 -0
  86. package/ai/llm/types.ts +59 -0
  87. package/ai/llm/usageRequestOptions.ts +59 -0
  88. package/ai/memory/capture.ts +148 -0
  89. package/ai/memory/consolidate.ts +104 -0
  90. package/ai/memory/delete.ts +147 -0
  91. package/ai/memory/overlay.ts +84 -0
  92. package/ai/memory/query.ts +38 -0
  93. package/ai/memory/queryShared.ts +160 -0
  94. package/ai/memory/rank.ts +105 -0
  95. package/ai/memory/recentRelationshipRecap.ts +247 -0
  96. package/ai/memory/remember.ts +167 -0
  97. package/ai/memory/runtime.ts +76 -0
  98. package/ai/memory/store.ts +20 -0
  99. package/ai/memory/storeShared.ts +76 -0
  100. package/ai/memory/types.ts +46 -0
  101. package/ai/memory/understanding.ts +349 -0
  102. package/ai/memory/understandingGreeting.ts +264 -0
  103. package/ai/messages/type.ts +20 -0
  104. package/ai/policy/personalizationDialog.ts +333 -0
  105. package/ai/policy/runtimePolicy.ts +440 -0
  106. package/ai/policy/selfUpdateFields.ts +48 -0
  107. package/ai/policy/types.ts +64 -0
  108. package/ai/skills/referenceRuntime.ts +274 -0
  109. package/ai/skills/skillDiagnostics.ts +251 -0
  110. package/ai/skills/skillDocBuilder.ts +139 -0
  111. package/ai/skills/skillDocProtocol.ts +434 -0
  112. package/ai/skills/skillReferenceSummary.ts +63 -0
  113. package/ai/skills/skillSummaryMarker.ts +26 -0
  114. package/ai/token/calculatePrice.ts +546 -0
  115. package/ai/token/db.ts +98 -0
  116. package/ai/token/externalToolCost.ts +321 -0
  117. package/ai/token/hooks/useRecords.ts +65 -0
  118. package/ai/token/missingUsageEstimate.ts +42 -0
  119. package/ai/token/modelUsageQuery.ts +252 -0
  120. package/ai/token/normalizeUsage.ts +84 -0
  121. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  122. package/ai/token/prepareTokenUsageData.ts +88 -0
  123. package/ai/token/query.ts +88 -0
  124. package/ai/token/queryUserTokens.ts +59 -0
  125. package/ai/token/resolveBillingTarget.ts +52 -0
  126. package/ai/token/saveTokenRecord.ts +53 -0
  127. package/ai/token/serverDialogProjection.ts +78 -0
  128. package/ai/token/serverTokenWriter.ts +143 -0
  129. package/ai/token/stats.ts +21 -0
  130. package/ai/token/tokenThunks.ts +24 -0
  131. package/ai/token/types.ts +93 -0
  132. package/ai/tools/agent/agentTools.ts +176 -0
  133. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  134. package/ai/tools/agent/callAgentTool.ts +139 -0
  135. package/ai/tools/agent/createAgentTool.ts +512 -0
  136. package/ai/tools/agent/createDialogTool.ts +69 -0
  137. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  138. package/ai/tools/agent/parallelBudget.ts +221 -0
  139. package/ai/tools/agent/presets/appBuilderPreset.ts +147 -0
  140. package/ai/tools/agent/runLlmTool.ts +96 -0
  141. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  142. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  143. package/ai/tools/agent/skillAgentPreset.ts +89 -0
  144. package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
  145. package/ai/tools/agent/updateAgentTool.ts +96 -0
  146. package/ai/tools/agent/updateSelfTool.ts +113 -0
  147. package/ai/tools/amazonProductScraperTool.ts +86 -0
  148. package/ai/tools/apifyActorClient.ts +45 -0
  149. package/ai/tools/appEditGuard.ts +372 -0
  150. package/ai/tools/appReadSnapshot.ts +153 -0
  151. package/ai/tools/appTools.ts +1549 -0
  152. package/ai/tools/applyEditTool.ts +256 -0
  153. package/ai/tools/applyLineEditsTool.ts +312 -0
  154. package/ai/tools/browserTools/click.ts +33 -0
  155. package/ai/tools/browserTools/closeSession.ts +29 -0
  156. package/ai/tools/browserTools/common.ts +27 -0
  157. package/ai/tools/browserTools/openSession.ts +48 -0
  158. package/ai/tools/browserTools/readContent.ts +38 -0
  159. package/ai/tools/browserTools/selectOption.ts +46 -0
  160. package/ai/tools/browserTools/typeText.ts +42 -0
  161. package/ai/tools/category/createCategoryTool.ts +66 -0
  162. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  163. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  164. package/ai/tools/cfBrowserTools.ts +319 -0
  165. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  166. package/ai/tools/checkEnvTool.ts +65 -0
  167. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  168. package/ai/tools/codeSearchTool.ts +111 -0
  169. package/ai/tools/codeTools.ts +101 -0
  170. package/ai/tools/createDocTool.ts +132 -0
  171. package/ai/tools/createPlanTool.ts +999 -0
  172. package/ai/tools/createSkillDocTool.ts +155 -0
  173. package/ai/tools/createWorkflowTool.ts +154 -0
  174. package/ai/tools/deepseekOcrTool.ts +34 -0
  175. package/ai/tools/delayTool.ts +31 -0
  176. package/ai/tools/deleteSpacesTool.ts +325 -0
  177. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  178. package/ai/tools/devReloadUtils.ts +29 -0
  179. package/ai/tools/dialogMessageSearch.ts +137 -0
  180. package/ai/tools/doctorSkillTool.ts +72 -0
  181. package/ai/tools/ecommerceScraperTool.ts +86 -0
  182. package/ai/tools/emailTools.ts +549 -0
  183. package/ai/tools/evalSkillTool.ts +92 -0
  184. package/ai/tools/exaSearchTool.ts +64 -0
  185. package/ai/tools/execBashTool.ts +379 -0
  186. package/ai/tools/executeSqlTool.ts +192 -0
  187. package/ai/tools/fetchWebpageSupport.ts +309 -0
  188. package/ai/tools/fetchWebpageTool.ts +84 -0
  189. package/ai/tools/geminiImagePreviewTool.ts +361 -0
  190. package/ai/tools/generateDocxTool.ts +215 -0
  191. package/ai/tools/googleSearchScraperTool.ts +106 -0
  192. package/ai/tools/importDataTool.ts +133 -0
  193. package/ai/tools/importSkillTool.ts +162 -0
  194. package/ai/tools/index.ts +1927 -0
  195. package/ai/tools/listFilesTool.ts +82 -0
  196. package/ai/tools/listUserSpacesTool.ts +113 -0
  197. package/ai/tools/modelUsageTools.ts +199 -0
  198. package/ai/tools/olmOcrTool.ts +34 -0
  199. package/ai/tools/openaiImageTool.ts +267 -0
  200. package/ai/tools/prepareTools.ts +23 -0
  201. package/ai/tools/readDocTool.ts +84 -0
  202. package/ai/tools/readFileTool.ts +211 -0
  203. package/ai/tools/readTool.ts +163 -0
  204. package/ai/tools/readXPostTool.ts +233 -0
  205. package/ai/tools/rememberMemoryTool.ts +84 -0
  206. package/ai/tools/remotionVideoTool.ts +151 -0
  207. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  208. package/ai/tools/searchRepoTool.ts +115 -0
  209. package/ai/tools/searchWorkspaceTool.ts +259 -0
  210. package/ai/tools/skillFollowup.ts +86 -0
  211. package/ai/tools/surfWeatherTool.ts +169 -0
  212. package/ai/tools/table/addTableRowTool.ts +217 -0
  213. package/ai/tools/table/createTableTool.ts +315 -0
  214. package/ai/tools/table/rowTools.ts +366 -0
  215. package/ai/tools/table/schemaTools.ts +244 -0
  216. package/ai/tools/table/shareTableTool.ts +148 -0
  217. package/ai/tools/table/toolShared.ts +129 -0
  218. package/ai/tools/toolApiClient.ts +198 -0
  219. package/ai/tools/toolNameAliases.ts +57 -0
  220. package/ai/tools/toolResultError.ts +42 -0
  221. package/ai/tools/toolRunSlice.ts +303 -0
  222. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  223. package/ai/tools/toolVisibility.ts +4 -0
  224. package/ai/tools/types.ts +20 -0
  225. package/ai/tools/uiAskChoiceTool.ts +104 -0
  226. package/ai/tools/updateContentTitleTool.ts +84 -0
  227. package/ai/tools/updateDocTool.ts +105 -0
  228. package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
  229. package/ai/tools/whisperTool.ts +77 -0
  230. package/ai/tools/writeFileTool.ts +210 -0
  231. package/ai/tools/youtubeScraperTool.ts +116 -0
  232. package/ai/tools/ziweiChartTool.ts +678 -0
  233. package/ai/types.ts +55 -0
  234. package/ai/workflow/workflowExecutor.ts +323 -0
  235. package/ai/workflow/workflowSlice.ts +73 -0
  236. package/ai/workflow/workflowTypes.ts +106 -0
  237. package/client/agentRun.test.ts +240 -0
  238. package/client/agentRun.ts +182 -19
  239. package/client/compactDialog.test.ts +238 -0
  240. package/client/localRuntimeAdapter.test.ts +135 -0
  241. package/client/localRuntimeAdapter.ts +244 -0
  242. package/client/profileConfig.test.ts +40 -0
  243. package/client/streamingOutput.test.ts +22 -0
  244. package/client/streamingOutput.ts +38 -0
  245. package/commandRegistry.ts +9 -2
  246. package/connector-experimental/index.ts +5 -0
  247. package/database/actions/cacheMergedUserData.ts +64 -0
  248. package/database/actions/common.ts +242 -0
  249. package/database/actions/deleteFile.ts +40 -0
  250. package/database/actions/fetchUserData.ts +16 -0
  251. package/database/actions/fileContent.ts +125 -0
  252. package/database/actions/patch.ts +155 -0
  253. package/database/actions/read.ts +337 -0
  254. package/database/actions/readAndWait.ts +224 -0
  255. package/database/actions/readRequestManager.ts +120 -0
  256. package/database/actions/remove.ts +94 -0
  257. package/database/actions/replication.ts +366 -0
  258. package/database/actions/upload.ts +174 -0
  259. package/database/actions/upsert.ts +56 -0
  260. package/database/actions/write.ts +126 -0
  261. package/database/client/db.native.ts +73 -0
  262. package/database/client/db.ts +51 -0
  263. package/database/client/fetchUserData.ts +61 -0
  264. package/database/client/handleError.ts +19 -0
  265. package/database/client/queryRequest.ts +21 -0
  266. package/database/config.ts +21 -0
  267. package/database/dbActionThunks.ts +1 -0
  268. package/database/dbSlice.ts +149 -0
  269. package/database/email.ts +42 -0
  270. package/database/fileRing.ts +51 -0
  271. package/database/fileSharding.ts +70 -0
  272. package/database/fileStorage.native.ts +92 -0
  273. package/database/fileStorage.ts +232 -0
  274. package/database/fileUrl.ts +34 -0
  275. package/database/hooks/useUserData.ts +489 -0
  276. package/database/index.ts +1 -0
  277. package/database/keys.ts +765 -0
  278. package/database/queryPrefixes.ts +14 -0
  279. package/database/requests.ts +443 -0
  280. package/database/runtimeServerContext.ts +35 -0
  281. package/database/server/MemoryDB.ts +76 -0
  282. package/database/server/actorAccess.ts +76 -0
  283. package/database/server/agentDelegation.ts +124 -0
  284. package/database/server/coreDataOwnership.ts +13 -0
  285. package/database/server/coreDataProxy.ts +76 -0
  286. package/database/server/cybotReadonly.ts +18 -0
  287. package/database/server/dataHandlers.ts +111 -0
  288. package/database/server/db.ts +118 -0
  289. package/database/server/dbPath.ts +20 -0
  290. package/database/server/delete.ts +499 -0
  291. package/database/server/emailRepository.ts +1480 -0
  292. package/database/server/ensureDbOpen.ts +12 -0
  293. package/database/server/fileRead.ts +337 -0
  294. package/database/server/fileService.ts +436 -0
  295. package/database/server/handleTransaction.ts +86 -0
  296. package/database/server/patch.ts +282 -0
  297. package/database/server/query.ts +138 -0
  298. package/database/server/read.ts +325 -0
  299. package/database/server/resourceAccess.ts +211 -0
  300. package/database/server/routes.ts +110 -0
  301. package/database/server/spaceMemberAuthority.ts +67 -0
  302. package/database/server/upload.ts +159 -0
  303. package/database/server/write.ts +494 -0
  304. package/database/server/writeAuthority.ts +133 -0
  305. package/database/sqliteDb.ts +46 -0
  306. package/database/table/deleteTable.ts +120 -0
  307. package/database/tenantPlacement.ts +57 -0
  308. package/database/tombstones.ts +52 -0
  309. package/database/userDataLoadDecision.ts +17 -0
  310. package/database/userDataMerge.ts +95 -0
  311. package/database/userPreferenceRegister.ts +108 -0
  312. package/database/utils/dbPath.ts +47 -0
  313. package/database/utils/ulid.native.ts +6 -0
  314. package/database/utils/ulid.ts +1 -0
  315. package/index.ts +25 -15
  316. package/localRuntimeDb.ts +28 -0
  317. package/package.json +16 -4
  318. package/runtimeModeArgs.ts +33 -0
  319. package/tui/readlineWorkspace.ts +1 -0
  320. package/tui/session.ts +22 -0
@@ -0,0 +1,120 @@
1
+ const DEFAULT_MISS_COOLDOWN_MS = 2000;
2
+ const DEFAULT_LOCAL_HIT_REVALIDATE_COOLDOWN_MS = 1500;
3
+ const DEFAULT_MISS_CACHE_MAX_SIZE = 1000;
4
+
5
+ export class ReadRequestManager {
6
+ private readonly inFlightReads = new Map<string, Promise<any>>();
7
+ private readonly recentMisses = new Map<string, number>();
8
+ private readonly recentLocalHitRevalidations = new Map<string, number>();
9
+
10
+ constructor(
11
+ private readonly options: {
12
+ missCooldownMs?: number;
13
+ missCacheMaxSize?: number;
14
+ localHitRevalidateCooldownMs?: number;
15
+ } = {}
16
+ ) {}
17
+
18
+ private get missCooldownMs() {
19
+ return this.options.missCooldownMs ?? DEFAULT_MISS_COOLDOWN_MS;
20
+ }
21
+
22
+ private get missCacheMaxSize() {
23
+ return this.options.missCacheMaxSize ?? DEFAULT_MISS_CACHE_MAX_SIZE;
24
+ }
25
+
26
+ private get localHitRevalidateCooldownMs() {
27
+ return (
28
+ this.options.localHitRevalidateCooldownMs ??
29
+ DEFAULT_LOCAL_HIT_REVALIDATE_COOLDOWN_MS
30
+ );
31
+ }
32
+
33
+ getInFlight(dbKey: string) {
34
+ return this.inFlightReads.get(dbKey);
35
+ }
36
+
37
+ setInFlight(dbKey: string, promise: Promise<any>) {
38
+ this.inFlightReads.set(dbKey, promise);
39
+ }
40
+
41
+ clearInFlight(dbKey: string, promise: Promise<any>) {
42
+ if (this.inFlightReads.get(dbKey) === promise) {
43
+ this.inFlightReads.delete(dbKey);
44
+ }
45
+ }
46
+
47
+ clearMiss(dbKey: string) {
48
+ this.recentMisses.delete(dbKey);
49
+ }
50
+
51
+ getRetryInMs(dbKey: string, now: number) {
52
+ const missUntil = this.recentMisses.get(dbKey);
53
+ if (typeof missUntil !== "number") return null;
54
+ if (missUntil <= now) {
55
+ this.recentMisses.delete(dbKey);
56
+ return null;
57
+ }
58
+ return missUntil - now;
59
+ }
60
+
61
+ markMiss(dbKey: string, now: number, cooldownMs = this.missCooldownMs) {
62
+ this.recentMisses.set(dbKey, now + cooldownMs);
63
+ this.cleanupMisses(now);
64
+ }
65
+
66
+ getLocalHitRevalidateInMs(dbKey: string, now: number) {
67
+ const nextAllowedAt = this.recentLocalHitRevalidations.get(dbKey);
68
+ if (typeof nextAllowedAt !== "number") return null;
69
+ if (nextAllowedAt <= now) {
70
+ this.recentLocalHitRevalidations.delete(dbKey);
71
+ return null;
72
+ }
73
+ return nextAllowedAt - now;
74
+ }
75
+
76
+ markLocalHitRevalidated(
77
+ dbKey: string,
78
+ now: number,
79
+ cooldownMs = this.localHitRevalidateCooldownMs
80
+ ) {
81
+ this.recentLocalHitRevalidations.set(dbKey, now + cooldownMs);
82
+ this.cleanupLocalHitRevalidations(now);
83
+ }
84
+
85
+ private cleanupExpiringMap(map: Map<string, number>, now: number) {
86
+ for (const [key, expiresAt] of Array.from(map.entries())) {
87
+ if (expiresAt <= now) {
88
+ map.delete(key);
89
+ }
90
+ }
91
+
92
+ if (map.size <= this.missCacheMaxSize) return;
93
+ const overflow = map.size - this.missCacheMaxSize;
94
+ const keys = Array.from(map.keys());
95
+ for (let i = 0; i < overflow; i += 1) {
96
+ const key = keys[i];
97
+ if (key) map.delete(key);
98
+ }
99
+ }
100
+
101
+ cleanupMisses(now: number) {
102
+ this.cleanupExpiringMap(this.recentMisses, now);
103
+ }
104
+
105
+ cleanupLocalHitRevalidations(now: number) {
106
+ this.cleanupExpiringMap(this.recentLocalHitRevalidations, now);
107
+ }
108
+
109
+ // test helper
110
+ getMissCacheSize() {
111
+ return this.recentMisses.size;
112
+ }
113
+
114
+ // test helper
115
+ getLocalHitRevalidationCacheSize() {
116
+ return this.recentLocalHitRevalidations.size;
117
+ }
118
+ }
119
+
120
+ export const readRequestManager = new ReadRequestManager();
@@ -0,0 +1,94 @@
1
+ // 文件路径: src/database/actions/delete.ts
2
+
3
+ import type { AppThunkApi } from "app/store";
4
+ import { getRuntimeServerContext } from "database/runtimeServerContext";
5
+
6
+ import { fetchFromClientDb } from "./common";
7
+ import { deleteFileFromIndexedDb } from "../fileStorage";
8
+ import {
9
+ scheduleDeleteReplication,
10
+ } from "./replication";
11
+ import { buildTombstoneRecord } from "../tombstones";
12
+
13
+ /**
14
+ * removeAction:
15
+ * 1. 先删除本地 IndexedDB 中的记录(如果存在)
16
+ * 2. 再异步并行通知所有远程服务器删除该记录
17
+ *
18
+ * - 服务器列表来源:
19
+ * - 当前服务器:settings.currentServer
20
+ * - 备份服务器:settings.syncServers
21
+ * - getAllServers 负责去重 + 离线检测(offline 时返回 [])
22
+ */
23
+ export const removeAction = async (
24
+ payload: string | { dbKey: string; preferredServerOrigin?: string | null },
25
+ thunkApi: AppThunkApi
26
+ ): Promise<{ dbKey: string }> => {
27
+ const { db: clientDb } = thunkApi.extra;
28
+ const dbKey = typeof payload === "string" ? payload : payload.dbKey;
29
+ const preferredServerOrigin =
30
+ typeof payload === "string" ? undefined : payload.preferredServerOrigin;
31
+
32
+ if (!clientDb) {
33
+ throw new Error("Client database is undefined in removeAction");
34
+ }
35
+
36
+ const state = thunkApi.getState();
37
+ const { currentServer, syncServers } = getRuntimeServerContext(state);
38
+
39
+ console.log("[removeAction] START", {
40
+ dbKey,
41
+ preferredServerOrigin,
42
+ currentServer,
43
+ syncServers,
44
+ hasToken: Boolean(state?.auth?.currentToken),
45
+ });
46
+
47
+ // 1) 先查本地是否有这条数据
48
+ const localData = await fetchFromClientDb(clientDb, dbKey);
49
+ const hadLocalData = Boolean(localData);
50
+
51
+ // 2) local mutation 只依赖本地真相;远端删除由 replication helper 负责后台收敛。
52
+ console.log("[removeAction] replication inputs", {
53
+ currentServer,
54
+ syncServers,
55
+ preferredServerOrigin,
56
+ hadLocalData,
57
+ });
58
+
59
+ // 3) 如果本地存在,则先写 tombstone 到本地。
60
+ // Recent / My Content 是多源 merge,直接物理删除本地会丢失“删除胜出”证据,
61
+ // 老版本远端返回的活记录会在下一轮 merge 时再次回流。
62
+ const nowIso = new Date().toISOString();
63
+ if (localData) {
64
+ if (localData.id && typeof localData.id === "string") {
65
+ void deleteFileFromIndexedDb(localData.id).catch((err) => {
66
+ console.warn("[removeAction] Failed to delete associated file:", localData.id, err);
67
+ });
68
+ }
69
+ await clientDb.put(dbKey, buildTombstoneRecord(localData, nowIso));
70
+ } else {
71
+ // 本地无数据(仅存于远端),写最小 tombstone 防止远端记录在 merge 时回流
72
+ await clientDb.put(dbKey, buildTombstoneRecord({ dbKey }, nowIso));
73
+ }
74
+
75
+ // 4) local-first 产品语义:本地 tombstone 立即成功,远端删除异步收敛。
76
+ // 这样离线/弱网时删除也能成立,UI 可立刻移除内容;远端复制后续自行收敛。
77
+ scheduleDeleteReplication({
78
+ currentServer,
79
+ syncServers,
80
+ preferredServerOrigin,
81
+ dbKey,
82
+ state,
83
+ onResult: (result) => {
84
+ if (result.failed.length > 0) {
85
+ console.warn("[removeAction] Server delete failures after local tombstone:", result.failed);
86
+ }
87
+ },
88
+ onError: (err) => {
89
+ console.warn("[removeAction] Background server delete error:", err);
90
+ },
91
+ });
92
+
93
+ return { dbKey };
94
+ };
@@ -0,0 +1,366 @@
1
+ import { getAllServers } from "./common";
2
+ import {
3
+ noloDeleteRequest,
4
+ noloPatchRequest,
5
+ noloUploadRequest,
6
+ noloWriteRequest,
7
+ syncWithServers,
8
+ } from "../requests";
9
+ import { planServersForTenant } from "../tenantPlacement";
10
+
11
+ const isReadonlyPublicRecordKey = (dbKey: string): boolean =>
12
+ dbKey.startsWith("agent-pub-") || dbKey.startsWith("cybot-pub-");
13
+
14
+ export const resolveReplicationServers = (
15
+ currentServer: string | undefined,
16
+ syncServers: string[] | undefined,
17
+ preferredServerOrigin?: string | null
18
+ ): string[] => getAllServers(currentServer, syncServers, preferredServerOrigin);
19
+
20
+ export const scheduleWriteReplication = (
21
+ servers: string[],
22
+ request: { data: any; customKey: string; userId?: string },
23
+ state: any
24
+ ) => {
25
+ if (servers.length === 0) return;
26
+ Promise.resolve().then(async () => {
27
+ const [primaryServer, ...backupServers] = servers;
28
+ const primarySucceeded = await noloWriteRequest(primaryServer, request, state);
29
+
30
+ if (!primarySucceeded) {
31
+ console.warn(`Primary write sync failed for ${request.customKey} on ${primaryServer}`);
32
+ }
33
+
34
+ if (backupServers.length === 0) {
35
+ return;
36
+ }
37
+
38
+ syncWithServers(
39
+ backupServers,
40
+ (server, requestConfig, requestState, signal) =>
41
+ noloWriteRequest(server, requestConfig, requestState, signal, {
42
+ failureLogLevel: "info",
43
+ }),
44
+ `Backup write sync failed for ${request.customKey} on`,
45
+ request,
46
+ state
47
+ );
48
+ });
49
+ };
50
+
51
+ export const resolveTenantReplicationServers = ({
52
+ currentServer,
53
+ syncServers,
54
+ tenantId,
55
+ }: {
56
+ currentServer: string | undefined;
57
+ syncServers: string[] | undefined;
58
+ tenantId: string | null | undefined;
59
+ }): string[] => {
60
+ const allServers = resolveReplicationServers(currentServer, syncServers);
61
+ if (allServers.length === 0) {
62
+ return [];
63
+ }
64
+
65
+ return planServersForTenant(allServers, currentServer, tenantId);
66
+ };
67
+
68
+ export const scheduleExistingRecordReplication = ({
69
+ currentServer,
70
+ syncServers,
71
+ preferredServerOrigin,
72
+ dbKey,
73
+ localData,
74
+ state,
75
+ }: {
76
+ currentServer: string | undefined;
77
+ syncServers: string[] | undefined;
78
+ preferredServerOrigin?: string | null;
79
+ dbKey: string;
80
+ localData: any;
81
+ state: any;
82
+ }): string[] => {
83
+ if (isReadonlyPublicRecordKey(dbKey)) {
84
+ return [];
85
+ }
86
+
87
+ const servers = resolveReplicationServers(
88
+ currentServer,
89
+ syncServers,
90
+ preferredServerOrigin
91
+ );
92
+ if (servers.length === 0) {
93
+ return [];
94
+ }
95
+
96
+ scheduleWriteReplication(
97
+ servers,
98
+ {
99
+ data: localData,
100
+ customKey: dbKey,
101
+ userId:
102
+ typeof localData?.userId === "string"
103
+ ? localData.userId
104
+ : state?.auth?.currentUser?.userId,
105
+ },
106
+ state
107
+ );
108
+
109
+ return servers;
110
+ };
111
+
112
+ export const schedulePatchReplication = ({
113
+ servers,
114
+ dbKey,
115
+ changes,
116
+ state,
117
+ preferredServerOrigin,
118
+ }: {
119
+ servers: string[];
120
+ dbKey: string;
121
+ changes: any;
122
+ state: any;
123
+ preferredServerOrigin?: string | null;
124
+ }) => {
125
+ if (servers.length === 0) return;
126
+
127
+ Promise.resolve().then(async () => {
128
+ const primaryServer =
129
+ typeof preferredServerOrigin === "string" && preferredServerOrigin.trim().length > 0
130
+ ? preferredServerOrigin.trim().replace(/\/+$/, "")
131
+ : servers[0];
132
+ const backupServers = servers.filter(
133
+ (server) => server.replace(/\/+$/, "") !== primaryServer
134
+ );
135
+
136
+ const primarySucceeded = await noloPatchRequest(primaryServer, dbKey, changes, state, undefined, {
137
+ failureLogLevel: "warn",
138
+ });
139
+ if (!primarySucceeded) {
140
+ console.warn(`Primary patch sync failed for ${dbKey} on ${primaryServer}`);
141
+ }
142
+
143
+ if (backupServers.length > 0) {
144
+ syncWithServers(
145
+ backupServers,
146
+ (server, targetDbKey, nextChanges, requestState, signal) =>
147
+ noloPatchRequest(server, targetDbKey, nextChanges, requestState, signal, {
148
+ failureLogLevel: "info",
149
+ }),
150
+ `Backup patch sync failed for ${dbKey} on`,
151
+ dbKey,
152
+ changes,
153
+ state
154
+ );
155
+ }
156
+ });
157
+ };
158
+
159
+ export const scheduleConfiguredPatchReplication = ({
160
+ currentServer,
161
+ syncServers,
162
+ preferredServerOrigin,
163
+ dbKey,
164
+ changes,
165
+ state,
166
+ }: {
167
+ currentServer: string | undefined;
168
+ syncServers: string[] | undefined;
169
+ preferredServerOrigin?: string | null;
170
+ dbKey: string;
171
+ changes: any;
172
+ state: any;
173
+ }): string[] => {
174
+ const servers = resolveReplicationServers(
175
+ currentServer,
176
+ syncServers,
177
+ preferredServerOrigin
178
+ );
179
+
180
+ if (servers.length === 0) {
181
+ return [];
182
+ }
183
+
184
+ schedulePatchReplication({
185
+ servers,
186
+ dbKey,
187
+ changes,
188
+ state,
189
+ preferredServerOrigin,
190
+ });
191
+
192
+ return servers;
193
+ };
194
+
195
+ export const scheduleUploadReplication = ({
196
+ currentServer,
197
+ syncServers,
198
+ tenantId,
199
+ uploadConfig,
200
+ state,
201
+ excludeServers = [],
202
+ }: {
203
+ currentServer: string | undefined;
204
+ syncServers: string[] | undefined;
205
+ tenantId: string | null | undefined;
206
+ uploadConfig: {
207
+ file: File;
208
+ metadata: any;
209
+ customKey: string;
210
+ userId?: string;
211
+ };
212
+ state: any;
213
+ excludeServers?: string[];
214
+ }): string[] => {
215
+ const servers = resolveTenantReplicationServers({
216
+ currentServer,
217
+ syncServers,
218
+ tenantId,
219
+ });
220
+ const excluded = new Set(
221
+ excludeServers
222
+ .filter((server): server is string => typeof server === "string")
223
+ .map((server) => server.trim().replace(/\/+$/, ""))
224
+ );
225
+ const remainingServers = servers.filter(
226
+ (server) => !excluded.has(server.trim().replace(/\/+$/, ""))
227
+ );
228
+
229
+ if (remainingServers.length === 0) {
230
+ return [];
231
+ }
232
+
233
+ Promise.resolve().then(() => {
234
+ syncWithServers(
235
+ remainingServers,
236
+ noloUploadRequest,
237
+ `Upload sync failed for ${uploadConfig.customKey} on`,
238
+ uploadConfig,
239
+ state
240
+ );
241
+ });
242
+
243
+ return remainingServers;
244
+ };
245
+
246
+ export const uploadToCurrentServer = async ({
247
+ currentServer,
248
+ uploadConfig,
249
+ state,
250
+ }: {
251
+ currentServer: string | undefined;
252
+ uploadConfig: {
253
+ file: File;
254
+ metadata: any;
255
+ customKey: string;
256
+ userId?: string;
257
+ };
258
+ state: any;
259
+ }): Promise<boolean> => {
260
+ if (!currentServer) {
261
+ return false;
262
+ }
263
+
264
+ return noloUploadRequest(currentServer, uploadConfig, state);
265
+ };
266
+
267
+ export const deleteFromReplicationServers = async ({
268
+ servers,
269
+ dbKey,
270
+ deleteOptions = { type: "single" as const },
271
+ state,
272
+ preferredServerOrigin,
273
+ }: {
274
+ servers: string[];
275
+ dbKey: string;
276
+ deleteOptions?: { type: "single" | "table" | "messages" };
277
+ state: any;
278
+ preferredServerOrigin?: string | null;
279
+ }): Promise<{ succeeded: string[]; failed: string[] }> => {
280
+ if (!servers.length) {
281
+ return { succeeded: [], failed: [] };
282
+ }
283
+
284
+ const preferredServer =
285
+ typeof preferredServerOrigin === "string" && preferredServerOrigin.trim().length > 0
286
+ ? preferredServerOrigin.trim().replace(/\/+$/, "")
287
+ : null;
288
+ const remainingServers = preferredServer
289
+ ? servers.filter((server) => server.replace(/\/+$/, "") !== preferredServer)
290
+ : servers;
291
+ const succeeded: string[] = [];
292
+ const failed: string[] = [];
293
+
294
+ if (preferredServer) {
295
+ const ok = await noloDeleteRequest(preferredServer, dbKey, deleteOptions, state);
296
+ if (ok) {
297
+ succeeded.push(preferredServer);
298
+ } else {
299
+ failed.push(preferredServer);
300
+ }
301
+ }
302
+
303
+ if (remainingServers.length > 0) {
304
+ const results = await Promise.all(
305
+ remainingServers.map(async (server) => ({
306
+ server,
307
+ ok: await noloDeleteRequest(server, dbKey, deleteOptions, state),
308
+ }))
309
+ );
310
+ results.forEach(({ server, ok }) => {
311
+ if (ok) succeeded.push(server);
312
+ else failed.push(server);
313
+ });
314
+ }
315
+
316
+ return { succeeded, failed };
317
+ };
318
+
319
+ export const scheduleDeleteReplication = ({
320
+ currentServer,
321
+ syncServers,
322
+ preferredServerOrigin,
323
+ dbKey,
324
+ deleteOptions,
325
+ state,
326
+ onResult,
327
+ onError,
328
+ }: {
329
+ currentServer: string | undefined;
330
+ syncServers: string[] | undefined;
331
+ preferredServerOrigin?: string | null;
332
+ dbKey: string;
333
+ deleteOptions?: { type: "single" | "table" | "messages" };
334
+ state: any;
335
+ onResult?: (result: { succeeded: string[]; failed: string[] }) => void;
336
+ onError?: (error: unknown) => void;
337
+ }): string[] => {
338
+ const servers = resolveReplicationServers(
339
+ currentServer,
340
+ syncServers,
341
+ preferredServerOrigin
342
+ );
343
+
344
+ if (servers.length === 0) {
345
+ return [];
346
+ }
347
+
348
+ void Promise.resolve()
349
+ .then(() =>
350
+ deleteFromReplicationServers({
351
+ servers,
352
+ dbKey,
353
+ deleteOptions,
354
+ state,
355
+ preferredServerOrigin,
356
+ })
357
+ )
358
+ .then((result) => {
359
+ onResult?.(result);
360
+ })
361
+ .catch((error) => {
362
+ onError?.(error);
363
+ });
364
+
365
+ return servers;
366
+ };