nolo-cli 0.1.29 → 0.1.31

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 (329) hide show
  1. package/agent-runtime/agentRecordConfig.ts +0 -2
  2. package/agent-runtime/hostAdapter.ts +0 -1
  3. package/agent-runtime/hostRuntimeCapabilities.ts +7 -0
  4. package/agent-runtime/hostRuntimeConfig.ts +86 -0
  5. package/agent-runtime/index.ts +13 -0
  6. package/agent-runtime/localToolPolicy.ts +13 -6
  7. package/agent-runtime/localWorkspaceTools.ts +333 -30
  8. package/agent-runtime/platformChatProvider.ts +4 -1
  9. package/agent-runtime/runtimeToolPolicy.ts +32 -47
  10. package/agent-runtime/shellCommandPolicy.ts +117 -0
  11. package/agent-runtime/toolGate.ts +38 -0
  12. package/agentCreateCustomCommands.ts +9 -4
  13. package/agentDialogsCommands.ts +4 -3
  14. package/agentDoctorCommands.ts +3 -3
  15. package/agentListCommands.ts +3 -3
  16. package/agentMachineCommands.ts +6 -6
  17. package/agentPullCommand.ts +4 -12
  18. package/agentRecordCommands.ts +5 -3
  19. package/agentRecordHelpers.ts +42 -23
  20. package/agentRunCommand.ts +2 -6
  21. package/agentRunParsing.ts +18 -5
  22. package/agentRuntimeLocal.ts +10 -0
  23. package/agentUnpublishCommands.ts +3 -2
  24. package/ai/agent/agentRunDialogLineage.ts +56 -0
  25. package/ai/agent/agentSlice.ts +45 -10
  26. package/ai/agent/avatarUtils.ts +1 -1
  27. package/ai/agent/buildSystemPrompt.ts +1 -2
  28. package/ai/agent/cliExecutor.ts +330 -19
  29. package/ai/agent/createAgentSchema.ts +22 -0
  30. package/ai/agent/hooks/fetchPublicAgents.ts +14 -39
  31. package/ai/agent/hooks/usePublicAgents.ts +90 -192
  32. package/ai/agent/publicAgentCatalog.ts +359 -0
  33. package/ai/agent/publicAgentIdentity.ts +76 -0
  34. package/ai/agent/server/fetchPublicAgents.ts +21 -46
  35. package/ai/agent/serverOrigin.ts +7 -0
  36. package/ai/agent/streamAgentChatTurn.ts +23 -21
  37. package/ai/agent/streamAgentChatTurnUtils.ts +292 -6
  38. package/ai/agent/types.ts +2 -0
  39. package/ai/agent/web/agentDisplayUtils.ts +171 -0
  40. package/ai/agent/web/useAgentModelAvatarComponent.ts +32 -0
  41. package/ai/ai.locale.ts +8 -0
  42. package/ai/chat/sendOpenAICompletionsRequest.native.ts +15 -0
  43. package/ai/chat/sendOpenAICompletionsRequest.ts +93 -1
  44. package/ai/chat/sendOpenAIResponseRequest.ts +86 -9
  45. package/ai/chat/updateTotalUsage.ts +7 -0
  46. package/ai/llm/deepinfra.ts +30 -15
  47. package/ai/llm/mistral.ts +3 -4
  48. package/ai/llm/providers.ts +2 -0
  49. package/ai/llm/types.ts +15 -0
  50. package/ai/memory/capture.ts +3 -3
  51. package/ai/memory/consolidate.ts +2 -2
  52. package/ai/memory/delete.ts +4 -3
  53. package/ai/memory/query.ts +3 -3
  54. package/ai/memory/remember.ts +2 -2
  55. package/ai/memory/store.ts +3 -3
  56. package/ai/memory/storeShared.ts +3 -2
  57. package/ai/policy/commercialMultiplier.ts +16 -0
  58. package/ai/token/billingAdminAuditEvent.ts +68 -0
  59. package/ai/token/billingAdminAuditEventWriter.ts +41 -0
  60. package/ai/token/billingAnomaly.ts +90 -0
  61. package/ai/token/billingAnomalyLifecycle.ts +54 -0
  62. package/ai/token/billingAnomalyLifecycleWriter.ts +44 -0
  63. package/ai/token/billingAnomalyWriter.ts +41 -0
  64. package/ai/token/calculatePrice.ts +3 -2
  65. package/ai/token/deepinfraReconciliation.ts +153 -0
  66. package/ai/token/deepinfraRequestIdQuery.ts +92 -0
  67. package/ai/token/externalToolCost.ts +54 -41
  68. package/ai/token/missingUsageEstimate.ts +12 -3
  69. package/ai/token/normalizeUsage.ts +45 -5
  70. package/ai/token/openaiReconciliation.ts +188 -0
  71. package/ai/token/providerBillableEvent.ts +84 -0
  72. package/ai/token/providerBillableEventWriter.ts +36 -0
  73. package/ai/token/providerBillingAnomalyDrilldown.ts +308 -0
  74. package/ai/token/providerBillingDrilldown.ts +361 -0
  75. package/ai/token/providerBillingHealthReport.ts +366 -0
  76. package/ai/token/providerCall.ts +144 -0
  77. package/ai/token/providerCallSweeper.ts +168 -0
  78. package/ai/token/providerCallWriter.ts +33 -0
  79. package/ai/token/providerCredential.ts +89 -0
  80. package/ai/token/providerCredentialAnomaly.ts +154 -0
  81. package/ai/token/providerCredentialLifecycle.ts +59 -0
  82. package/ai/token/providerCredentialLifecycleWriter.ts +44 -0
  83. package/ai/token/providerCredentialRegistry.ts +83 -0
  84. package/ai/token/providerCredentialRegistryCommand.ts +97 -0
  85. package/ai/token/providerCredentialRegistryList.ts +124 -0
  86. package/ai/token/providerCredentialRegistryResolver.ts +150 -0
  87. package/ai/token/providerCredentialRegistryWriter.ts +41 -0
  88. package/ai/token/providerCredentialRotationCommand.ts +102 -0
  89. package/ai/token/providerCredentialRuntime.ts +56 -0
  90. package/ai/token/providerDispatchIntent.ts +67 -0
  91. package/ai/token/providerDispatchIntentWriter.ts +36 -0
  92. package/ai/token/providerGateway.ts +260 -0
  93. package/ai/token/providerReconciliation.ts +148 -0
  94. package/ai/token/providerReconciliationLocal.ts +97 -0
  95. package/ai/token/providerReconciliationRunner.ts +165 -0
  96. package/ai/token/providerReconciliationWriter.ts +41 -0
  97. package/ai/token/ratingResult.ts +202 -0
  98. package/ai/token/ratingResultWriter.ts +33 -0
  99. package/ai/token/serverDialogProjection.ts +5 -3
  100. package/ai/token/serverTokenWriter.ts +11 -3
  101. package/ai/token/types.ts +10 -0
  102. package/ai/tools/agent/callAgentTool.ts +3 -4
  103. package/ai/tools/agent/presets/appBuilderPreset.ts +5 -7
  104. package/ai/tools/agent/runStreamingAgentTool.ts +1 -2
  105. package/ai/tools/agent/streamParallelAgentsTool.ts +2 -3
  106. package/ai/tools/readXPostTool.ts +1 -5
  107. package/app/admin/adminPages.ts +9 -0
  108. package/app/i18n/i18n.config.ts +5 -0
  109. package/app/i18n/translations/pricing.locale.ts +397 -0
  110. package/app/utils/desktopAgentRuntimeStatusClient.ts +5 -4
  111. package/app/utils/desktopAgentRuntimeTurnClient.ts +4 -1
  112. package/app/utils/imageUtils.ts +64 -34
  113. package/auth/adminPermissions.ts +18 -0
  114. package/auth/authSlice.ts +26 -12
  115. package/auth/hooks/useFetchUsers.ts +3 -0
  116. package/auth/routes.ts +46 -0
  117. package/auth/server/accessActivity.ts +4 -3
  118. package/auth/server/adminPermissions.ts +43 -0
  119. package/auth/server/adminUsageReport.ts +375 -0
  120. package/auth/server/billingAnomalies.ts +235 -0
  121. package/auth/server/billingUsageReport.ts +41 -2
  122. package/auth/server/creatorEarningCreditConversion.ts +10 -2
  123. package/auth/server/deduct.ts +14 -7
  124. package/auth/server/delete.ts +10 -2
  125. package/auth/server/disable.ts +4 -3
  126. package/auth/server/emailAutomation.ts +7 -5
  127. package/auth/server/emailDbConfig.ts +4 -2
  128. package/auth/server/emailDelivery.ts +24 -20
  129. package/auth/server/emailDeliveryGuards.ts +10 -7
  130. package/auth/server/emailPreferences.ts +6 -4
  131. package/auth/server/emailReport.ts +6 -4
  132. package/auth/server/emailRuntimeConfig.ts +3 -3
  133. package/auth/server/emailScenarioNotifications.ts +9 -8
  134. package/auth/server/enable.ts +4 -3
  135. package/auth/server/getRechargeHistory.ts +3 -2
  136. package/auth/server/getUser.ts +3 -2
  137. package/auth/server/gptProAccess.ts +44 -0
  138. package/auth/server/inviteReward.ts +10 -7
  139. package/auth/server/listusers.ts +6 -4
  140. package/auth/server/login.ts +2 -2
  141. package/auth/server/productEntitlements.ts +7 -4
  142. package/auth/server/productPurchase.ts +10 -2
  143. package/auth/server/providerBillingDrilldown.ts +47 -0
  144. package/auth/server/providerBillingHealth.ts +71 -0
  145. package/auth/server/providerCredentials.ts +136 -0
  146. package/auth/server/recharge.ts +14 -8
  147. package/auth/server/revokeSessions.ts +4 -3
  148. package/auth/server/route.ts +77 -0
  149. package/auth/server/sendEmail.ts +5 -4
  150. package/auth/server/signup.ts +10 -3
  151. package/auth/server/token.ts +53 -55
  152. package/auth/server/tokenUsageBilling.ts +18 -7
  153. package/auth/server/usageManagementAccess.ts +43 -0
  154. package/auth/server/usageReport.ts +59 -0
  155. package/auth/types.ts +12 -0
  156. package/auth/utils.ts +24 -22
  157. package/authCommands.ts +36 -2
  158. package/buildPublish.ts +3 -1
  159. package/buildPublishArtifactAssembly.ts +62 -1
  160. package/buildPublishImportAnalysis.ts +21 -16
  161. package/chat/dialog/dialogLaunchScope.ts +32 -0
  162. package/chat/dialog/objectAssistantRegistry.ts +2 -2
  163. package/chat/dialog/useCreateDialog.ts +1 -1
  164. package/chat/hooks/useChatInput.ts +41 -10
  165. package/chat/messages/messageSlice.ts +5 -1
  166. package/chat/messages/pendingAttachmentParts.ts +28 -2
  167. package/chat/messages/sendFirstMessage.ts +76 -79
  168. package/chat/messages/types.ts +9 -0
  169. package/chat/messages/web/streamingMarkdownModel.ts +41 -0
  170. package/chat/messages/web/useStreamingReveal.ts +91 -0
  171. package/chat/web/messageInputAgentUi.ts +94 -6
  172. package/cliEnvHelpers.ts +68 -13
  173. package/cliRuntimeDoctorReport.ts +193 -0
  174. package/client/agentConfigResolver.test.ts +0 -2
  175. package/client/agentRun.test.ts +171 -10
  176. package/client/agentRun.ts +44 -637
  177. package/client/agentRunInput.ts +10 -0
  178. package/client/agentRunLocalExecutor.ts +179 -0
  179. package/client/agentRunServerTransport.ts +394 -0
  180. package/client/agentRunTaskRunRecorder.ts +176 -0
  181. package/client/localRuntimeAdapter.test.ts +236 -9
  182. package/client/localRuntimeAdapter.ts +221 -23
  183. package/client/localToolPolicy.test.ts +28 -4
  184. package/connector-experimental/daemonLauncher.ts +38 -4
  185. package/connectorRunArtifact.ts +34 -10
  186. package/connectorWebSocketTarget.ts +40 -15
  187. package/core/builtinAgents.ts +155 -11
  188. package/create/space/contentLabels.ts +1 -1
  189. package/create/space/contentTypeMeta.ts +3 -2
  190. package/create/space/sidebarVisibleTypes.ts +27 -0
  191. package/create/space/space.locale.ts +76 -4
  192. package/database/actions/agentReadResolution.ts +2 -8
  193. package/database/actions/read.ts +11 -9
  194. package/database/actions/readAndWait.ts +10 -5
  195. package/database/server/agentDelegation.ts +3 -2
  196. package/database/server/authorityStoreConformance.ts +209 -0
  197. package/database/server/authorityStoreTypes.ts +31 -0
  198. package/database/server/coreDataProxy.ts +1 -1
  199. package/database/server/dataHandlers.ts +13 -5
  200. package/database/server/db.ts +15 -34
  201. package/database/server/delete.ts +88 -71
  202. package/database/server/emailRepository.ts +11 -9
  203. package/database/server/fileService.ts +2 -1
  204. package/database/server/legacyServerDb.ts +58 -0
  205. package/database/server/levelAuthorityStore.ts +75 -0
  206. package/database/server/memoryAuthorityStore.ts +133 -0
  207. package/database/server/patch.ts +13 -12
  208. package/database/server/query.ts +3 -2
  209. package/database/server/read.ts +33 -30
  210. package/database/server/resourceAccess.ts +7 -5
  211. package/database/server/serverStoreFactory.ts +124 -0
  212. package/database/server/spaceMemberAuthority.ts +3 -2
  213. package/database/server/storeBatchCompat.ts +37 -0
  214. package/database/server/testAuthorityStore.ts +76 -0
  215. package/database/server/write.ts +29 -27
  216. package/dialogReadCommands.ts +2 -1
  217. package/dialogStatusCommands.ts +2 -1
  218. package/integrations/anthropic/anthropicModels.ts +12 -12
  219. package/integrations/deepseek/models.ts +67 -67
  220. package/integrations/google/models.ts +111 -53
  221. package/integrations/openai/models.ts +22 -22
  222. package/integrations/xai/models.ts +1 -1
  223. package/localRuntimeDb.ts +39 -2
  224. package/machineCommandPrompts.ts +2 -0
  225. package/machineCommands.ts +193 -62
  226. package/machineStatusCommands.ts +4 -12
  227. package/machineWsRunDispatch.ts +80 -15
  228. package/machineWsSession.ts +46 -2
  229. package/noloReadBridgePrompt.ts +16 -0
  230. package/package.json +114 -21
  231. package/pageCreateCommands.ts +2 -2
  232. package/pageDeleteCommands.ts +3 -3
  233. package/pageReadCommands.ts +3 -3
  234. package/pageUpdateCommands.ts +3 -3
  235. package/render/layout/mainLayoutViewMode.ts +25 -0
  236. package/render/page/server/createPage.ts +3 -1
  237. package/render/page/server/handleCreatePage.ts +2 -11
  238. package/runtimeDoctorCommands.ts +18 -69
  239. package/server/api/methods.ts +34 -12
  240. package/server/api/tableHandlers.ts +6 -3
  241. package/server/authenticateToken.ts +47 -2
  242. package/server/billingLedgerRoutes.ts +15 -0
  243. package/server/buildManifest.ts +54 -0
  244. package/server/coreReadiness.ts +34 -25
  245. package/server/coreRuntime.ts +15 -10
  246. package/server/handlers/activityStatsHandler.ts +5 -2
  247. package/server/handlers/agentEmailRegistrationE2EHandler.ts +25 -23
  248. package/server/handlers/agentRun/agentLookup.ts +1 -7
  249. package/server/handlers/agentRun/autonomousRuntime.ts +11 -10
  250. package/server/handlers/agentRun/builtinAgents.ts +12 -88
  251. package/server/handlers/agentRun/continueDialogContext.ts +15 -3
  252. package/server/handlers/agentRun/{inheritedDialogPrompt.ts → conversationReference.ts} +3 -3
  253. package/server/handlers/agentRun/dialogLookup.ts +4 -3
  254. package/server/handlers/agentRun/index.ts +864 -404
  255. package/server/handlers/agentRun/localRuntimeBootstrap.ts +17 -1
  256. package/server/handlers/agentRun/loop.ts +920 -508
  257. package/server/handlers/agentRun/machineConnector.ts +56 -8
  258. package/server/handlers/agentRun/machineRunAudit.ts +3 -2
  259. package/server/handlers/agentRun/modelUsageToolHandlers.ts +13 -10
  260. package/server/handlers/agentRun/requestContext.ts +12 -8
  261. package/server/handlers/agentRun/runtimeExecutionContext.ts +15 -3
  262. package/server/handlers/agentRun/runtimePreparation.ts +26 -24
  263. package/server/handlers/agentRun/runtimeRouting.ts +25 -40
  264. package/server/handlers/agentRun/runtimeSystemMessages.ts +65 -0
  265. package/server/handlers/agentRun/saveDialog.ts +97 -87
  266. package/server/handlers/agentRun/taskRunToolHandler.ts +9 -7
  267. package/server/handlers/agentRun/toolExecutor.ts +42 -78
  268. package/server/handlers/agentRun/types.ts +2 -1
  269. package/server/handlers/appDeployStatusStore.ts +17 -10
  270. package/server/handlers/appDomainHandler.ts +21 -15
  271. package/server/handlers/appLegacyInfraCleanup.ts +5 -2
  272. package/server/handlers/appSandboxHandler.ts +29 -8
  273. package/server/handlers/appSpaceAccess.ts +15 -8
  274. package/server/handlers/chatHandler.ts +22 -2
  275. package/server/handlers/chatUpstreamRetry.ts +225 -2
  276. package/server/handlers/connector/connectorConnectionRegistry.ts +55 -7
  277. package/server/handlers/connector/connectorWebSocket.ts +14 -10
  278. package/server/handlers/desktopAgentRuntimeAdapter.ts +4 -4
  279. package/server/handlers/desktopAgentRuntimeHostCapabilities.ts +25 -0
  280. package/server/handlers/desktopAgentRuntimeReadiness.ts +62 -0
  281. package/server/handlers/desktopAgentRuntimeStatusHandler.ts +38 -30
  282. package/server/handlers/desktopAgentRuntimeTurnHandler.ts +7 -3
  283. package/server/handlers/desktopAgentRuntimeTurnService.ts +39 -45
  284. package/server/handlers/desktopLlamaRuntimeHandler.ts +85 -51
  285. package/server/handlers/desktopUpdaterHandler.ts +132 -24
  286. package/server/handlers/favoriteHandlers.ts +8 -2
  287. package/server/handlers/getDialogBillingHandler.ts +4 -3
  288. package/server/handlers/getTransactionsHandler.ts +5 -2
  289. package/server/handlers/machines/machineBinding.ts +1 -0
  290. package/server/handlers/machines/machineHandlers.ts +0 -14
  291. package/server/handlers/machines/machineInstallAssets.ts +4 -2
  292. package/server/handlers/notificationEmitter.ts +5 -3
  293. package/server/handlers/notificationsHandler.ts +5 -2
  294. package/server/handlers/providerCallsRecentHandler.ts +234 -0
  295. package/server/handlers/providerReconciliationRunHandler.ts +180 -0
  296. package/server/handlers/providerRequestIdsHandler.ts +105 -0
  297. package/server/handlers/tableAccess.ts +6 -2
  298. package/server/handlers/tableToolHandlers.ts +28 -14
  299. package/server/handlers/userSecretsHandler.ts +17 -6
  300. package/server/handlers/versionHandler.ts +9 -4
  301. package/server/handlers/versionStore.ts +18 -10
  302. package/server/html/ssrPreload.ts +5 -3
  303. package/server/payments/crypto/cryptoUsdcBasePaymentProvider.ts +11 -8
  304. package/server/payments/crypto/cryptoUsdcBaseScanner.ts +12 -7
  305. package/server/payments/crypto/cryptoUsdtTronPaymentProvider.ts +11 -8
  306. package/server/payments/crypto/cryptoUsdtTronScanner.ts +9 -7
  307. package/server/payments/waffo/waffoPaymentProvider.ts +12 -9
  308. package/server/runtimeRole.ts +15 -0
  309. package/server/scheduler/scheduledTaskRuntime.ts +4 -2
  310. package/server/scheduler/schedulerService.ts +2 -2
  311. package/server/taskRunBridgeRoutes.ts +8 -5
  312. package/share/server/fetchCommunityShares.ts +1 -1
  313. package/share/server/helpers.ts +19 -17
  314. package/share/server/importShare.ts +9 -13
  315. package/share/server/listCommunityShares.ts +4 -4
  316. package/share/server/purchaseDoc.ts +8 -16
  317. package/share/server/readCommunityShareCover.ts +3 -2
  318. package/share/server/readSharedTable.ts +14 -13
  319. package/share/server/tableReplication.ts +42 -39
  320. package/spaceAttachCommands.ts +135 -0
  321. package/spaceCategoryCommands.ts +6 -6
  322. package/spaceCommands.ts +3 -3
  323. package/spaceDeleteCommands.ts +2 -2
  324. package/spaceInternalCommandEntries.ts +2 -0
  325. package/spaceReadCommands.ts +3 -3
  326. package/tableMetaCommands.ts +2 -2
  327. package/taskRunCommand.ts +11 -0
  328. package/ai/agent/runtimeServerBase.ts +0 -37
  329. package/server/handlers/desktopAgentRuntimeHostFacts.ts +0 -40
@@ -1,4 +1,5 @@
1
1
  import type { AgentRuntimeMessageContent } from "../../agent-runtime";
2
+ import { normalizeAgentRunDialogLineage } from "../../ai/agent/agentRunDialogLineage";
2
3
  import { parseToken } from "../../auth/token";
3
4
  import { WEB_AUTH_TOKEN_COOKIE } from "../../auth/tokenCookie";
4
5
  import {
@@ -7,7 +8,7 @@ import {
7
8
  type DesktopTextOnlyAgentRuntimeTurnInput,
8
9
  } from "./desktopAgentRuntimeTurnService";
9
10
  import type { DesktopAgentRuntimeRecordStore } from "./desktopAgentRuntimeAdapter";
10
- import type { DesktopAgentRuntimeEnv } from "./desktopAgentRuntimeHostFacts";
11
+ import type { DesktopAgentRuntimeEnv } from "./desktopAgentRuntimeHostCapabilities";
11
12
 
12
13
  type DesktopAgentRuntimeTurnBody = {
13
14
  agentRef: string;
@@ -87,11 +88,14 @@ function parseDesktopAgentRuntimeTurnBody(body: unknown): DesktopAgentRuntimeTur
87
88
  const record = body as Record<string, unknown>;
88
89
  if (typeof record.agentRef !== "string" || !record.agentRef.trim()) return null;
89
90
  if (!isAgentRuntimeMessageContent(record.input)) return null;
91
+ const dialogLineage = normalizeAgentRunDialogLineage({
92
+ continueDialogId: record.continueDialogId,
93
+ });
90
94
  return {
91
95
  agentRef: record.agentRef.trim(),
92
96
  input: record.input,
93
- ...(typeof record.continueDialogId === "string" && record.continueDialogId.trim()
94
- ? { continueDialogId: record.continueDialogId.trim() }
97
+ ...(dialogLineage.continueDialogId
98
+ ? { continueDialogId: dialogLineage.continueDialogId }
95
99
  : {}),
96
100
  ...(typeof record.maxToolRounds === "number" && Number.isFinite(record.maxToolRounds)
97
101
  ? { maxToolRounds: Math.max(0, Math.floor(record.maxToolRounds)) }
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  AgentRuntimeAgentConfig,
3
+ HostRuntimeConfig,
3
4
  AgentRuntimeMessageContent,
4
5
  AgentRuntimeProvider,
5
6
  AgentRuntimeToolCallInput,
@@ -13,11 +14,14 @@ import {
13
14
  createWorkspaceSession,
14
15
  executeLocalToolWithPolicy,
15
16
  prepareGitTaskWorkspace,
17
+ resolveHostRuntimeConfigFromEnv,
18
+ resolveRequestedRuntimeToolNames,
16
19
  runLocalAgentTurn,
17
20
  } from "../../agent-runtime";
18
21
  import {
19
22
  buildLocalWorkspaceOpenAiTools,
20
23
  buildLocalWorkspacePolicyToolNames,
24
+ resolveLocalWorkspaceShellToolExposure,
21
25
  buildLocalWorkspaceToolset,
22
26
  createLocalWorkspaceToolExecutors,
23
27
  } from "../../agent-runtime/localWorkspaceTools";
@@ -28,10 +32,11 @@ import {
28
32
  type DesktopAgentRuntimeActions,
29
33
  type DesktopAgentRuntimeRecordStore,
30
34
  } from "./desktopAgentRuntimeAdapter";
31
- import type { DesktopAgentRuntimeEnv } from "./desktopAgentRuntimeHostFacts";
35
+ import type { DesktopAgentRuntimeEnv } from "./desktopAgentRuntimeHostCapabilities";
32
36
 
33
37
  type DesktopAgentRuntimeBaseTurnInput = {
34
38
  env: DesktopAgentRuntimeEnv;
39
+ hostRuntimeConfig?: HostRuntimeConfig;
35
40
  store: DesktopAgentRuntimeRecordStore;
36
41
  agentRef: string;
37
42
  input: AgentRuntimeMessageContent;
@@ -75,8 +80,8 @@ export function createDesktopAgentRuntimeDialogId() {
75
80
  }
76
81
 
77
82
  function buildDesktopOpenAiTools(args: {
78
- toolNames?: string[];
79
- env: DesktopAgentRuntimeEnv;
83
+ requestedToolNames?: string[];
84
+ hostRuntimeConfig: HostRuntimeConfig;
80
85
  }) {
81
86
  const toolset = buildDesktopLocalWorkspaceToolset(args);
82
87
  return buildLocalWorkspaceOpenAiTools({
@@ -86,29 +91,31 @@ function buildDesktopOpenAiTools(args: {
86
91
  }
87
92
 
88
93
  function buildDesktopLocalWorkspaceToolset(args: {
89
- toolNames?: string[];
90
- env: DesktopAgentRuntimeEnv;
94
+ requestedToolNames?: string[];
95
+ hostRuntimeConfig: HostRuntimeConfig;
91
96
  }) {
92
- const shellModeEnabled = ["worktree", "dangerous", "1", "true"].includes(
93
- args.env.NOLO_LOCAL_SHELL_MODE || ""
94
- );
97
+ const requestedToolNames = args.requestedToolNames ?? [];
98
+ const exposeShellTools = resolveLocalWorkspaceShellToolExposure({
99
+ declaredToolNames: requestedToolNames,
100
+ hostRuntimeConfig: args.hostRuntimeConfig,
101
+ });
95
102
  const toolset = buildLocalWorkspaceToolset({
96
- declaredToolNames: args.toolNames,
97
- exposeShellTools: shellModeEnabled,
103
+ declaredToolNames: requestedToolNames,
104
+ exposeShellTools,
98
105
  });
99
106
  return toolset;
100
107
  }
101
108
 
102
109
  function buildDesktopLocalPolicyToolNames(args: {
103
- toolNames?: string[];
104
- env: DesktopAgentRuntimeEnv;
110
+ requestedToolNames?: string[];
111
+ hostRuntimeConfig: HostRuntimeConfig;
105
112
  }) {
106
- const shellModeEnabled = ["worktree", "dangerous", "1", "true"].includes(
107
- args.env.NOLO_LOCAL_SHELL_MODE || ""
108
- );
109
113
  return buildLocalWorkspacePolicyToolNames({
110
- declaredToolNames: args.toolNames,
111
- exposeShellTools: shellModeEnabled,
114
+ declaredToolNames: args.requestedToolNames,
115
+ exposeShellTools: resolveLocalWorkspaceShellToolExposure({
116
+ declaredToolNames: args.requestedToolNames,
117
+ hostRuntimeConfig: args.hostRuntimeConfig,
118
+ }),
112
119
  });
113
120
  }
114
121
 
@@ -120,23 +127,6 @@ function buildDesktopLocalToolExecutors(args: {
120
127
  };
121
128
  }
122
129
 
123
- function resolveDesktopRuntimePolicyEnv(args: {
124
- env: DesktopAgentRuntimeEnv;
125
- agentConfig: AgentRuntimeAgentConfig;
126
- }): DesktopAgentRuntimeEnv {
127
- const policy = args.agentConfig.runtimeToolPolicy;
128
- const shellPolicy = policy?.shell;
129
- const enablesWorktreeShell =
130
- shellPolicy?.enabled === true && shellPolicy.mode === "worktree";
131
- if (!enablesWorktreeShell || args.env.NOLO_LOCAL_SHELL_MODE) {
132
- return args.env;
133
- }
134
- return {
135
- ...args.env,
136
- NOLO_LOCAL_SHELL_MODE: "worktree",
137
- };
138
- }
139
-
140
130
  function resolveDesktopRuntimeWorkspaceMode(args: {
141
131
  agentConfig: AgentRuntimeAgentConfig;
142
132
  hasExplicitCwd: boolean;
@@ -236,6 +226,7 @@ export function createDesktopTextOnlyAgentRuntimeActions(args: {
236
226
 
237
227
  export function createDesktopAgentRuntimeActions(args: {
238
228
  env: DesktopAgentRuntimeEnv;
229
+ hostRuntimeConfig?: HostRuntimeConfig;
239
230
  store: DesktopAgentRuntimeRecordStore;
240
231
  now: () => number;
241
232
  createId: () => string;
@@ -244,7 +235,11 @@ export function createDesktopAgentRuntimeActions(args: {
244
235
  prepareTaskWorkspace?: PrepareTaskWorkspace;
245
236
  }): DesktopAgentRuntimeActions {
246
237
  let activeAgentToolNames: string[] = [];
247
- let activeEnv = args.env;
238
+ const hostRuntimeConfig = args.hostRuntimeConfig ?? resolveHostRuntimeConfigFromEnv({
239
+ env: args.env,
240
+ surface: "desktop",
241
+ workspaceBinding: args.cwd ? "explicit-cwd" : "default-cwd",
242
+ });
248
243
  let workspaceSession = createWorkspaceSession({ cwd: args.cwd });
249
244
  let executors = buildDesktopLocalToolExecutors({ workspaceRoot: workspaceSession.workspaceRoot });
250
245
  return createDesktopAgentRuntimeRecordStoreActions({
@@ -254,13 +249,10 @@ export function createDesktopAgentRuntimeActions(args: {
254
249
  createId: args.createId,
255
250
  getWorkspaceRoot: () => workspaceSession.workspaceRoot,
256
251
  resolveProvider: async (agentConfig) => {
257
- activeEnv = resolveDesktopRuntimePolicyEnv({
258
- env: args.env,
259
- agentConfig,
260
- });
252
+ const requestedToolNames = resolveRequestedRuntimeToolNames({ agentConfig });
261
253
  activeAgentToolNames = buildDesktopLocalPolicyToolNames({
262
- toolNames: agentConfig.toolNames,
263
- env: activeEnv,
254
+ requestedToolNames,
255
+ hostRuntimeConfig,
264
256
  });
265
257
  const workspaceMode = resolveDesktopRuntimeWorkspaceMode({
266
258
  agentConfig,
@@ -272,7 +264,7 @@ export function createDesktopAgentRuntimeActions(args: {
272
264
  workspaceSession = await activateWorkspaceSession(activationSession, {
273
265
  agentKey: agentConfig.key,
274
266
  mode: workspaceMode,
275
- env: activeEnv,
267
+ env: args.env,
276
268
  prepareTaskWorkspace: args.prepareTaskWorkspace ?? ((prepareArgs) => prepareGitTaskWorkspace({
277
269
  ...prepareArgs,
278
270
  cwd: args.cwd,
@@ -286,13 +278,14 @@ export function createDesktopAgentRuntimeActions(args: {
286
278
  agentConfig,
287
279
  fetchImpl: args.fetchImpl,
288
280
  tools: buildDesktopOpenAiTools({
289
- toolNames: agentConfig.toolNames,
290
- env: activeEnv,
281
+ requestedToolNames,
282
+ hostRuntimeConfig,
291
283
  }),
292
284
  });
293
285
  },
294
286
  executeTool: (call) => executeLocalToolWithPolicy({
295
- env: activeEnv,
287
+ env: args.env,
288
+ hostRuntimeConfig,
296
289
  agentToolNames: activeAgentToolNames,
297
290
  call,
298
291
  executors,
@@ -329,6 +322,7 @@ export async function runDesktopAgentRuntimeTurn(
329
322
  ): Promise<LocalAgentTurnResult> {
330
323
  const actions = createDesktopAgentRuntimeActions({
331
324
  env: input.env,
325
+ hostRuntimeConfig: input.hostRuntimeConfig,
332
326
  store: input.store,
333
327
  now: input.now ?? Date.now,
334
328
  createId: input.createId ?? createDesktopAgentRuntimeDialogId,
@@ -1,5 +1,6 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { mkdir, rm, writeFile } from "node:fs/promises";
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { mkdir, rm, writeFile } from "node:fs/promises";
3
+ import { dirname, join, posix, win32 } from "node:path";
3
4
 
4
5
  import { isPidRunning } from "../../../scripts/helpers/devProcessGuard";
5
6
  import {
@@ -22,10 +23,12 @@ const JSON_HEADERS = {
22
23
  "content-type": "application/json; charset=utf-8",
23
24
  };
24
25
 
25
- type DesktopLlamaRuntimeResponse = Omit<DesktopLlamaRuntimeSnapshot, "state"> & {
26
- state: DesktopLlamaRuntimeSnapshot["state"] | "starting";
27
- accepted?: boolean;
28
- };
26
+ type DesktopLlamaRuntimeResponse = Omit<DesktopLlamaRuntimeSnapshot, "state"> & {
27
+ state: DesktopLlamaRuntimeSnapshot["state"] | "starting";
28
+ accepted?: boolean;
29
+ };
30
+
31
+ export type DesktopProviderRuntimeSnapshot = DesktopLlamaRuntimeResponse;
29
32
 
30
33
  type DesktopLlamaRuntimeHandlerController = {
31
34
  status: () => Promise<DesktopLlamaRuntimeSnapshot>;
@@ -36,12 +39,13 @@ type DesktopLlamaRuntimeHandlerController = {
36
39
  }>;
37
40
  };
38
41
 
39
- type DesktopLlamaRuntimeHandlerDeps = {
40
- isDesktopMode?: () => boolean;
41
- controller?: DesktopLlamaRuntimeHandlerController;
42
- logger?: Pick<Console, "error">;
43
- repoRoot?: string;
44
- };
42
+ type DesktopLlamaRuntimeHandlerDeps = {
43
+ isDesktopMode?: () => boolean;
44
+ controller?: DesktopLlamaRuntimeHandlerController;
45
+ logger?: Pick<Console, "error">;
46
+ repoRoot?: string;
47
+ env?: NodeJS.ProcessEnv;
48
+ };
45
49
 
46
50
  const json = (data: unknown, status = 200) =>
47
51
  new Response(JSON.stringify(data), {
@@ -49,7 +53,7 @@ const json = (data: unknown, status = 200) =>
49
53
  headers: JSON_HEADERS,
50
54
  });
51
55
 
52
- const defaultIsDesktopMode = () => process.env.NOLO_DESKTOP === "1";
56
+ const defaultIsDesktopMode = () => process.env.NOLO_DESKTOP === "1";
53
57
 
54
58
  const notDesktopResponse = () =>
55
59
  json({ error: "Desktop llama runtime is only available inside Nolo Desktop." }, 404);
@@ -66,11 +70,21 @@ export function configureDesktopLocalProviderEnv(config: Pick<LlamaLaunchConfig,
66
70
  function formatError(error: unknown, fallback: string): string {
67
71
  return error instanceof Error ? error.message : fallback;
68
72
  }
69
-
70
- function createDesktopLlamaRuntimeController(
71
- repoRoot = process.cwd(),
72
- ): DesktopLlamaRuntimeHandlerController {
73
- const paths = resolveLlamaSupervisorPaths(repoRoot);
73
+
74
+ export function resolveDesktopLlamaSupervisorLogDir(env: NodeJS.ProcessEnv): string | null {
75
+ const dbPath = env.NOLO_SERVER_DB_PATH?.trim();
76
+ if (!dbPath) return null;
77
+ const pathApi = dbPath.includes("\\") ? win32 : posix;
78
+ const channelDir = pathApi.dirname(pathApi.dirname(dbPath));
79
+ return pathApi.join(channelDir, "logs", "llama-supervisor");
80
+ }
81
+
82
+ function createDesktopLlamaRuntimeController(
83
+ repoRoot = process.cwd(),
84
+ env: NodeJS.ProcessEnv = process.env,
85
+ ): DesktopLlamaRuntimeHandlerController {
86
+ const desktopLogDir = resolveDesktopLlamaSupervisorLogDir(env);
87
+ const paths = resolveLlamaSupervisorPaths(desktopLogDir ? { logDir: desktopLogDir } : repoRoot);
74
88
 
75
89
  const ensureLogDir = async () => {
76
90
  await mkdir(paths.logDir, { recursive: true });
@@ -168,9 +182,24 @@ function createDesktopLlamaRuntimeController(
168
182
  return config;
169
183
  };
170
184
 
171
- const probeHealth = (
172
- config: Pick<LlamaLaunchConfig, "host" | "port" | "healthPath" | "modelsPath">,
173
- ) => probeLlamaHealth(config);
185
+ const probeHealth = (
186
+ config: Pick<LlamaLaunchConfig, "host" | "port" | "healthPath" | "modelsPath">,
187
+ ) => probeLlamaHealth(config);
188
+
189
+ const resolveConfiguredLaunchConfig = async (): Promise<LlamaLaunchConfig | null> => {
190
+ const storedState = await readStoredState();
191
+ const localConfig = await readLocalConfigFile();
192
+ const config = resolveLlamaLaunchConfig({
193
+ storedConfig: {
194
+ ...(storedState?.launchConfig ?? {}),
195
+ ...(localConfig ?? {}),
196
+ },
197
+ });
198
+ if (config) {
199
+ configureDesktopLocalProviderEnv(config);
200
+ }
201
+ return config;
202
+ };
174
203
 
175
204
  const stopManagedServer = () =>
176
205
  stopManagedLlamaServer({
@@ -189,9 +218,9 @@ function createDesktopLlamaRuntimeController(
189
218
  logFilePath: paths.logFile,
190
219
  });
191
220
 
192
- const runtimeController = createLlamaRuntimeController({
193
- readStoredState,
194
- readLocalConfigFile,
221
+ const runtimeController = createLlamaRuntimeController({
222
+ readStoredState,
223
+ readLocalConfigFile,
195
224
  readManagedPid,
196
225
  readWatchPid,
197
226
  isPidRunning,
@@ -200,14 +229,17 @@ function createDesktopLlamaRuntimeController(
200
229
  readLogTail: () => readLogTail(40),
201
230
  });
202
231
 
203
- return {
204
- status: () => runtimeController.status(),
205
- stop: () => runtimeController.stop(),
206
- start: () =>
207
- startLlamaRuntime({
208
- resolveLaunchConfigOrThrow,
209
- writeStoredState,
210
- probeHealth,
232
+ return {
233
+ status: async () => {
234
+ await resolveConfiguredLaunchConfig();
235
+ return runtimeController.status();
236
+ },
237
+ stop: () => runtimeController.stop(),
238
+ start: () =>
239
+ startLlamaRuntime({
240
+ resolveLaunchConfigOrThrow,
241
+ writeStoredState,
242
+ probeHealth,
211
243
  readManagedPid,
212
244
  isPidRunning,
213
245
  stopManagedServer,
@@ -249,13 +281,13 @@ function buildResponseSnapshot(
249
281
  return snapshot;
250
282
  }
251
283
 
252
- export function createDesktopLlamaRuntimeHandlers(
253
- deps: DesktopLlamaRuntimeHandlerDeps = {},
254
- ) {
255
- const isDesktopMode = deps.isDesktopMode ?? defaultIsDesktopMode;
256
- const runtimeController =
257
- deps.controller ?? createDesktopLlamaRuntimeController(deps.repoRoot);
258
- const logger = deps.logger ?? console;
284
+ export function createDesktopLlamaRuntimeHandlers(
285
+ deps: DesktopLlamaRuntimeHandlerDeps = {},
286
+ ) {
287
+ const isDesktopMode = deps.isDesktopMode ?? defaultIsDesktopMode;
288
+ const runtimeController =
289
+ deps.controller ?? createDesktopLlamaRuntimeController(deps.repoRoot, deps.env);
290
+ const logger = deps.logger ?? console;
259
291
 
260
292
  let activeStartTask: Promise<void> | null = null;
261
293
  let activeStartToken = 0;
@@ -267,8 +299,8 @@ export function createDesktopLlamaRuntimeHandlers(
267
299
  lastStartError = null;
268
300
  };
269
301
 
270
- const readSnapshot = async () =>
271
- buildResponseSnapshot(await runtimeController.status(), activeStartTask, lastStartError);
302
+ const readSnapshot = async () =>
303
+ buildResponseSnapshot(await runtimeController.status(), activeStartTask, lastStartError);
272
304
 
273
305
  const trackStartTask = (task: Promise<void>) => {
274
306
  const taskToken = ++activeStartToken;
@@ -348,13 +380,15 @@ export function createDesktopLlamaRuntimeHandlers(
348
380
  return json({ error: "Unsupported action" }, 400);
349
381
  };
350
382
 
351
- return {
352
- handleGet,
353
- handlePost,
354
- };
355
- }
356
-
357
- const defaultHandlers = createDesktopLlamaRuntimeHandlers();
358
-
359
- export const handleDesktopLlamaRuntimeGet = defaultHandlers.handleGet;
360
- export const handleDesktopLlamaRuntimePost = defaultHandlers.handlePost;
383
+ return {
384
+ readSnapshot,
385
+ handleGet,
386
+ handlePost,
387
+ };
388
+ }
389
+
390
+ const defaultHandlers = createDesktopLlamaRuntimeHandlers();
391
+
392
+ export const readDesktopProviderRuntimeSnapshot = defaultHandlers.readSnapshot;
393
+ export const handleDesktopLlamaRuntimeGet = defaultHandlers.handleGet;
394
+ export const handleDesktopLlamaRuntimePost = defaultHandlers.handlePost;
@@ -1,9 +1,25 @@
1
+ import {
2
+ normalizeDesktopReleaseManifest,
3
+ type DesktopReleasePlatform,
4
+ } from "../../app/constants/desktopReleaseManifest";
5
+ import { createDesktopUpdaterSnapshot } from "../../desktop/desktopUpdaterState";
6
+ import type { DesktopUpdaterReleaseArtifact } from "../../desktop/desktopUpdatePolicy";
7
+
1
8
  const JSON_HEADERS = {
2
9
  "content-type": "application/json; charset=utf-8",
3
10
  };
4
11
 
5
12
  let activeOperation: "check" | "download" | "apply" | null = null;
6
13
  let activeTask: Promise<void> | null = null;
14
+ const DESKTOP_MANIFEST_CACHE_TTL_MS = 30_000;
15
+ const manifestCache = new Map<
16
+ string,
17
+ {
18
+ fetchedAt: number;
19
+ artifact: DesktopUpdaterReleaseArtifact | null;
20
+ error: string | null;
21
+ }
22
+ >();
7
23
 
8
24
  const isDesktopMode = () => process.env.NOLO_DESKTOP === "1";
9
25
 
@@ -17,12 +33,70 @@ const notDesktopResponse = () =>
17
33
  json({ error: "Desktop updater is only available inside Nolo Desktop." }, 404);
18
34
 
19
35
  const loadDesktopRuntime = async () => import("electrobun/bun");
36
+ type DesktopRuntimeModule = Awaited<ReturnType<typeof loadDesktopRuntime>>;
37
+ type UpdaterInstance = DesktopRuntimeModule["Updater"];
38
+
39
+ const resolveDesktopReleasePlatform = (): DesktopReleasePlatform =>
40
+ process.platform === "win32"
41
+ ? "windows"
42
+ : process.platform === "darwin"
43
+ ? "macos"
44
+ : "linux";
45
+
46
+ const resolveDesktopManifestUrl = (baseUrl: string) =>
47
+ `${baseUrl.replace(/\/+$/, "")}/desktop-release-manifest.json`;
20
48
 
21
49
  const latestStatus = (Updater: Awaited<ReturnType<typeof loadDesktopRuntime>>["Updater"]) => {
22
50
  const history = Updater.getStatusHistory();
23
51
  return history.length > 0 ? history[history.length - 1] : null;
24
52
  };
25
53
 
54
+ const readPublishedReleaseArtifact = async (
55
+ baseUrl: string,
56
+ platform: DesktopReleasePlatform
57
+ ) => {
58
+ const cacheKey = `${platform}:${baseUrl.replace(/\/+$/, "")}`;
59
+ const cached = manifestCache.get(cacheKey);
60
+ if (cached && Date.now() - cached.fetchedAt < DESKTOP_MANIFEST_CACHE_TTL_MS) {
61
+ return cached;
62
+ }
63
+
64
+ try {
65
+ const response = await fetch(resolveDesktopManifestUrl(baseUrl), {
66
+ method: "GET",
67
+ headers: {
68
+ accept: "application/json",
69
+ },
70
+ });
71
+ if (!response.ok) {
72
+ throw new Error(`HTTP ${response.status}`);
73
+ }
74
+
75
+ const manifest = normalizeDesktopReleaseManifest(await response.json());
76
+ if (!manifest) {
77
+ throw new Error("invalid manifest payload");
78
+ }
79
+
80
+ const artifact = manifest.artifacts[platform];
81
+ const result = {
82
+ fetchedAt: Date.now(),
83
+ artifact: artifact ? { platform, ...artifact } : null,
84
+ error: artifact ? null : `manifest is missing a ${platform} artifact`,
85
+ };
86
+ manifestCache.set(cacheKey, result);
87
+ return result;
88
+ } catch (error) {
89
+ const result = {
90
+ fetchedAt: Date.now(),
91
+ artifact: null,
92
+ error:
93
+ error instanceof Error ? error.message : "failed to fetch desktop release manifest",
94
+ };
95
+ manifestCache.set(cacheKey, result);
96
+ return result;
97
+ }
98
+ };
99
+
26
100
  const getUpdaterSnapshot = async () => {
27
101
  const { BuildConfig, Updater } = await loadDesktopRuntime();
28
102
  const [buildConfig, version, hash, channel, baseUrl] = await Promise.all([
@@ -32,9 +106,16 @@ const getUpdaterSnapshot = async () => {
32
106
  Updater.localInfo.channel(),
33
107
  Updater.localInfo.baseUrl(),
34
108
  ]);
35
-
36
- return {
109
+ const platform = resolveDesktopReleasePlatform();
110
+ const updateInfo = Updater.updateInfo?.() ?? null;
111
+ const shouldLoadReleaseArtifact = Boolean(updateInfo?.updateAvailable || updateInfo?.updateReady);
112
+ const releaseArtifactState = shouldLoadReleaseArtifact
113
+ ? await readPublishedReleaseArtifact(baseUrl, platform)
114
+ : { artifact: null, error: null };
115
+
116
+ return createDesktopUpdaterSnapshot({
37
117
  desktop: true,
118
+ platform,
38
119
  activeOperation,
39
120
  localInfo: {
40
121
  version,
@@ -43,10 +124,12 @@ const getUpdaterSnapshot = async () => {
43
124
  baseUrl,
44
125
  },
45
126
  buildConfig,
46
- updateInfo: Updater.updateInfo?.() ?? null,
127
+ updateInfo,
47
128
  latestStatus: latestStatus(Updater),
48
129
  statusHistory: Updater.getStatusHistory(),
49
- };
130
+ releaseArtifact: releaseArtifactState.artifact,
131
+ manifestError: releaseArtifactState.error,
132
+ });
50
133
  };
51
134
 
52
135
  const runOperation = (
@@ -79,6 +162,22 @@ const runOperation = (
79
162
  return true;
80
163
  };
81
164
 
165
+ const OPERATION_EXECUTORS: Record<
166
+ "check" | "download" | "apply",
167
+ { resetHistory?: boolean; run: (updater: UpdaterInstance) => Promise<void> }
168
+ > = {
169
+ check: {
170
+ resetHistory: true,
171
+ run: (updater) => updater.checkForUpdate(),
172
+ },
173
+ download: {
174
+ run: (updater) => updater.downloadUpdate(),
175
+ },
176
+ apply: {
177
+ run: (updater) => updater.applyUpdate(),
178
+ },
179
+ };
180
+
82
181
  export const handleDesktopUpdaterGet = async () => {
83
182
  if (!isDesktopMode()) return notDesktopResponse();
84
183
 
@@ -104,27 +203,36 @@ export const handleDesktopUpdaterPost = async (req: Request) => {
104
203
 
105
204
  const action = body.action;
106
205
 
107
- if (action === "check") {
108
- runOperation("check", async () => {
109
- const { Updater } = await loadDesktopRuntime();
110
- await Updater.checkForUpdate();
111
- }, true);
112
- return json(await getUpdaterSnapshot(), 202);
113
- }
114
-
115
- if (action === "download") {
116
- runOperation("download", async () => {
117
- const { Updater } = await loadDesktopRuntime();
118
- await Updater.downloadUpdate();
119
- });
120
- return json(await getUpdaterSnapshot(), 202);
121
- }
206
+ if (action === "check" || action === "download" || action === "apply") {
207
+ const snapshot = await getUpdaterSnapshot();
208
+ if (action !== "check" && snapshot.summary.primaryAction !== action) {
209
+ return json(
210
+ {
211
+ error:
212
+ snapshot.summary.statusMessage ??
213
+ `Desktop updater is not ready to ${action} right now.`,
214
+ },
215
+ 409
216
+ );
217
+ }
122
218
 
123
- if (action === "apply") {
124
- runOperation("apply", async () => {
125
- const { Updater } = await loadDesktopRuntime();
126
- await Updater.applyUpdate();
127
- });
219
+ const operation = OPERATION_EXECUTORS[action];
220
+ const started = runOperation(
221
+ action,
222
+ async () => {
223
+ const { Updater } = await loadDesktopRuntime();
224
+ await operation.run(Updater);
225
+ },
226
+ operation.resetHistory ?? false
227
+ );
228
+ if (!started) {
229
+ return json(
230
+ {
231
+ error: `Desktop updater is already busy with ${activeOperation ?? "another operation"}.`,
232
+ },
233
+ 409
234
+ );
235
+ }
128
236
  return json(await getUpdaterSnapshot(), 202);
129
237
  }
130
238
 
@@ -1,10 +1,11 @@
1
1
  // server/api/favoriteHandlers.ts
2
2
 
3
- import serverDb from "../../database/server/db";
3
+ import { getServerAuthorityStore } from "../../database/server/db";
4
4
  import {
5
5
  createFavoriteKey,
6
6
  createFavoriteStatsKey,
7
7
  } from "../../database/keys";
8
+ import { writeStoreOps } from "../../database/server/storeBatchCompat";
8
9
  import { isTombstoneRecord } from "../../database/tombstones";
9
10
 
10
11
  import type { ApiContext, ApiError } from "../api/types";
@@ -21,6 +22,8 @@ import {
21
22
  ListFavoritesResult,
22
23
  } from "../../app/favorite/type";
23
24
 
25
+ const getFavoriteStore = () => getServerAuthorityStore();
26
+
24
27
  function toTimeMs(t: unknown) {
25
28
  if (typeof t === "number") return t;
26
29
  const n = Date.parse(String(t ?? 0));
@@ -109,6 +112,7 @@ function resolveFavoriteKeysByKey(
109
112
 
110
113
  /** 安全 get:不存在时返回 undefined,而不是抛错 */
111
114
  async function safeGet<T>(key: string): Promise<T | undefined> {
115
+ const serverDb = getFavoriteStore();
112
116
  try {
113
117
  const record = await serverDb.get(key);
114
118
  return isTombstoneRecord(record) ? undefined : record;
@@ -138,6 +142,7 @@ async function setFavoriteState(
138
142
  desiredFavoriteState: boolean,
139
143
  favoritedAt?: number
140
144
  ): Promise<ToggleFavoriteResult> {
145
+ const serverDb = getFavoriteStore();
141
146
  const resolved = resolveFavoriteKeysByKey(targetType, userId, targetKey);
142
147
  const {
143
148
  relKey,
@@ -213,7 +218,7 @@ async function setFavoriteState(
213
218
  }
214
219
  }
215
220
 
216
- await serverDb.batch(ops);
221
+ await writeStoreOps(serverDb, ops);
217
222
 
218
223
  return buildFavoriteResult(
219
224
  targetType,
@@ -302,6 +307,7 @@ async function listFavoritesForUser(
302
307
  targetType: ListableFavoriteTargetType,
303
308
  userId: string
304
309
  ): Promise<ListFavoritesResult> {
310
+ const serverDb = getFavoriteStore();
305
311
  const normalizedTargetType =
306
312
  targetType === "doc" || targetType === "content" ? "page" : targetType;
307
313