nolo-cli 0.1.23 → 0.1.29

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 (931) hide show
  1. package/README.md +25 -3
  2. package/agent-runtime/dialogStatus.ts +139 -0
  3. package/agent-runtime/hybridRecordStore.ts +30 -9
  4. package/agent-runtime/index.ts +8 -0
  5. package/agent-runtime/localLoop.ts +12 -4
  6. package/agent-runtime/localToolPolicy.ts +22 -10
  7. package/agent-runtime/localWorkspaceTools.ts +140 -374
  8. package/agent-runtime/runtimeToolPolicy.ts +43 -2
  9. package/agent-runtime/types.ts +5 -0
  10. package/agentAliases.ts +12 -15
  11. package/agentCommandSupport.ts +77 -0
  12. package/agentCreateCustomCommands.ts +273 -0
  13. package/agentDialogsCommands.ts +226 -0
  14. package/agentDoctorCommands.ts +203 -0
  15. package/agentInternalCommandEntries.ts +43 -0
  16. package/agentListCommands.ts +132 -0
  17. package/agentListHelpers.ts +187 -0
  18. package/agentMachineCommands.ts +476 -0
  19. package/agentPullCommand.ts +9 -9
  20. package/agentRecordCommands.ts +141 -0
  21. package/agentRecordHelpers.ts +452 -0
  22. package/agentRunCommand.ts +35 -411
  23. package/agentRunLocalWorkspace.ts +182 -0
  24. package/agentRunParsing.ts +279 -0
  25. package/agentRuntimeCommands.ts +7 -701
  26. package/agentSuperviseCommands.ts +8 -0
  27. package/agentUnpublishCommands.ts +112 -0
  28. package/ai/agent/_executeModel.ts +42 -16
  29. package/ai/agent/agentSlice.ts +8 -8
  30. package/ai/agent/appWorkingMemory.ts +2 -2
  31. package/ai/agent/avatarUtils.ts +1 -1
  32. package/ai/agent/buildEditingContext.ts +3 -3
  33. package/ai/agent/buildSystemPrompt.ts +32 -6
  34. package/ai/agent/cleanAgentMessages.ts +1 -1
  35. package/ai/agent/cliChatClient.ts +41 -7
  36. package/ai/agent/cliExecutor.ts +308 -27
  37. package/ai/agent/createAgentSchema.ts +3 -3
  38. package/ai/agent/executeToolCall.ts +2 -2
  39. package/ai/agent/fetchAgentContexts.ts +3 -3
  40. package/ai/agent/getFullChatContextKeys.ts +4 -4
  41. package/ai/agent/guidedCreation/assistant.ts +82 -0
  42. package/ai/agent/guidedCreation/capabilities.ts +84 -0
  43. package/ai/agent/guidedCreation/draft.ts +62 -0
  44. package/ai/agent/guidedCreation/types.ts +38 -0
  45. package/ai/agent/hooks/fetchPublicAgents.ts +6 -6
  46. package/ai/agent/hooks/useAgentConfig.ts +49 -21
  47. package/ai/agent/hooks/useAgentDialog.ts +1 -1
  48. package/ai/agent/hooks/useAgentFormValidation.ts +9 -9
  49. package/ai/agent/hooks/usePublicAgents.ts +9 -9
  50. package/ai/agent/machineRunPermissions.ts +21 -3
  51. package/ai/agent/persistMessageWithFixedId.ts +4 -4
  52. package/ai/agent/planSlice.ts +1 -1
  53. package/ai/agent/referenceUtils.ts +4 -4
  54. package/ai/agent/runAgentBackground.ts +3 -3
  55. package/ai/agent/runAgentClientLoop.ts +12 -12
  56. package/ai/agent/runtimeGuidance.ts +2 -2
  57. package/ai/agent/server/fetchPublicAgents.ts +12 -7
  58. package/ai/agent/startParallelAgentStreams.ts +12 -12
  59. package/ai/agent/streamAgentChatTurn.ts +509 -36
  60. package/ai/agent/streamAgentChatTurnUtils.ts +22 -22
  61. package/ai/agent/utils/imageOutput.ts +2 -2
  62. package/ai/agent/utils/publicImageAgentMode.ts +1 -1
  63. package/ai/agent/utils/sortUtils.ts +1 -1
  64. package/ai/agent/web/useGuidedAgentCreation.ts +238 -0
  65. package/ai/ai.locale.ts +14 -2
  66. package/ai/chat/fetchUtils.native.ts +2 -2
  67. package/ai/chat/fetchUtils.ts +2 -2
  68. package/ai/chat/sendOpenAICompletionsRequest.native.ts +11 -11
  69. package/ai/chat/sendOpenAICompletionsRequest.ts +40 -15
  70. package/ai/chat/sendOpenAIResponseRequest.ts +17 -16
  71. package/ai/chat/shouldUseServerProxy.ts +1 -1
  72. package/ai/chat/updateTotalUsage.ts +1 -1
  73. package/ai/context/buildReferenceContext.ts +14 -14
  74. package/ai/context/calculateContextUsage.ts +1 -1
  75. package/ai/llm/generateRequestBody.ts +3 -3
  76. package/ai/llm/getPricing.ts +18 -4
  77. package/ai/llm/hooks/useModelPricing.ts +2 -2
  78. package/ai/llm/imagePricing.ts +1 -1
  79. package/ai/llm/kimi.ts +3 -0
  80. package/ai/llm/modelAvatar.ts +1 -0
  81. package/ai/llm/models.ts +3 -3
  82. package/ai/llm/providers.ts +9 -9
  83. package/ai/memory/capture.ts +1 -1
  84. package/ai/memory/consolidate.ts +1 -1
  85. package/ai/memory/delete.ts +2 -2
  86. package/ai/memory/query.ts +1 -1
  87. package/ai/memory/queryShared.ts +1 -1
  88. package/ai/memory/recentRelationshipRecap.ts +2 -2
  89. package/ai/memory/remember.ts +1 -1
  90. package/ai/memory/store.ts +1 -1
  91. package/ai/memory/storeShared.ts +2 -2
  92. package/ai/memory/understanding.ts +1 -1
  93. package/ai/messages/type.ts +1 -1
  94. package/ai/policy/personalizationDialog.ts +6 -6
  95. package/ai/policy/runtimePolicy.ts +1 -1
  96. package/ai/skills/referenceRuntime.ts +1 -1
  97. package/ai/skills/skillDiagnostics.ts +1 -1
  98. package/ai/skills/skillDocBuilder.ts +1 -1
  99. package/ai/skills/skillReferenceSummary.ts +1 -1
  100. package/ai/skills/skillSummaryMarker.ts +1 -1
  101. package/ai/taskRun/taskRunProtocol.ts +24 -10
  102. package/ai/token/calculatePrice.ts +5 -5
  103. package/ai/token/db.ts +3 -3
  104. package/ai/token/externalToolCost.ts +4 -4
  105. package/ai/token/hooks/useRecords.ts +3 -3
  106. package/ai/token/modelUsageQuery.ts +19 -2
  107. package/ai/token/openaiImageGenerationUsage.ts +2 -2
  108. package/ai/token/prepareTokenUsageData.ts +1 -1
  109. package/ai/token/query.ts +1 -1
  110. package/ai/token/queryUserTokens.ts +1 -1
  111. package/ai/token/resolveBillingTarget.ts +1 -1
  112. package/ai/token/saveTokenRecord.ts +5 -5
  113. package/ai/token/serverDialogProjection.ts +4 -4
  114. package/ai/token/serverTokenWriter.ts +3 -3
  115. package/ai/token/tokenThunks.ts +1 -1
  116. package/ai/tools/agent/agentTools.ts +21 -0
  117. package/ai/tools/agent/agentUpdateShared.ts +4 -4
  118. package/ai/tools/agent/callAgentTool.ts +3 -3
  119. package/ai/tools/agent/createAgentTool.ts +12 -12
  120. package/ai/tools/agent/createDialogTool.ts +3 -3
  121. package/ai/tools/agent/parallelBudget.ts +2 -2
  122. package/ai/tools/agent/prepareAgentDraftTool.ts +173 -0
  123. package/ai/tools/agent/presets/appBuilderPreset.ts +1 -1
  124. package/ai/tools/agent/runLlmTool.ts +1 -1
  125. package/ai/tools/agent/skillAgentPreset.ts +6 -6
  126. package/ai/tools/agent/updateAgentTool.ts +4 -4
  127. package/ai/tools/agent/updateSelfTool.ts +8 -8
  128. package/ai/tools/appTools.ts +3 -3
  129. package/ai/tools/category/createCategoryTool.ts +2 -2
  130. package/ai/tools/category/queryContentsByCategoryTool.ts +2 -2
  131. package/ai/tools/category/updateContentCategoryTool.ts +2 -2
  132. package/ai/tools/checkEnvTool.ts +52 -20
  133. package/ai/tools/convertMarxistsBookTool.ts +103 -0
  134. package/ai/tools/createDocTool.ts +3 -3
  135. package/ai/tools/createPlanTool.ts +9 -9
  136. package/ai/tools/createSkillDocTool.ts +4 -4
  137. package/ai/tools/createWorkflowTool.ts +2 -2
  138. package/ai/tools/deleteSpacesTool.ts +5 -5
  139. package/ai/tools/deleteSpacesToolModel.ts +1 -1
  140. package/ai/tools/doctorSkillTool.ts +3 -3
  141. package/ai/tools/evalSkillTool.ts +3 -3
  142. package/ai/tools/{execBashTool.ts → execShellTool.ts} +44 -161
  143. package/ai/tools/executeSqlTool.ts +1 -1
  144. package/ai/tools/geminiImagePreviewTool.ts +9 -9
  145. package/ai/tools/importDataTool.ts +2 -2
  146. package/ai/tools/importSkillTool.ts +4 -4
  147. package/ai/tools/index.ts +61 -14
  148. package/ai/tools/listUserSpacesTool.ts +2 -2
  149. package/ai/tools/localFilesTool.ts +85 -9
  150. package/ai/tools/marxistsOfflineBook.ts +253 -0
  151. package/ai/tools/modelUsageTools.ts +4 -2
  152. package/ai/tools/openaiImageTool.ts +9 -9
  153. package/ai/tools/readDocTool.ts +3 -3
  154. package/ai/tools/readTool.ts +40 -9
  155. package/ai/tools/rememberMemoryTool.ts +3 -3
  156. package/ai/tools/remotionVideoTool.ts +7 -7
  157. package/ai/tools/searchDialogMessagesTool.ts +2 -2
  158. package/ai/tools/searchWorkspaceTool.ts +4 -4
  159. package/ai/tools/table/addTableRowTool.ts +2 -2
  160. package/ai/tools/table/createTableTool.ts +2 -2
  161. package/ai/tools/table/rowTools.ts +2 -2
  162. package/ai/tools/table/schemaTools.ts +3 -3
  163. package/ai/tools/table/shareTableTool.ts +5 -5
  164. package/ai/tools/table/toolShared.ts +6 -6
  165. package/ai/tools/toolApiClient.ts +49 -6
  166. package/ai/tools/toolNameAliases.ts +13 -0
  167. package/ai/tools/toolRunSlice.ts +2 -2
  168. package/ai/tools/updateContentTitleTool.ts +2 -2
  169. package/ai/tools/updateDocTool.ts +7 -7
  170. package/ai/tools/updateUserPreferenceProfileTool.ts +3 -3
  171. package/ai/workflow/workflowExecutor.ts +3 -3
  172. package/ai/workflow/workflowSlice.ts +1 -1
  173. package/app/actions/syncAppRecord.ts +73 -0
  174. package/app/admin/adminPages.ts +13 -0
  175. package/app/colors.ts +1 -0
  176. package/app/constants/animationSets.ts +21 -0
  177. package/app/constants/appEditor.ts +99 -0
  178. package/app/constants/clientDownloads.ts +54 -0
  179. package/app/constants/desktopReleaseManifest.ts +68 -0
  180. package/app/constants/homeTabs.ts +15 -0
  181. package/app/constants/routePaths.ts +15 -0
  182. package/app/email/emailApiRoutes.ts +14 -0
  183. package/app/favorite/favoriteSlice.ts +690 -0
  184. package/app/favorite/type.ts +43 -0
  185. package/app/favorite/useAgentFavorite.ts +43 -0
  186. package/app/favorite/useContentFavorite.ts +41 -0
  187. package/app/fetchOwnedApps.ts +269 -0
  188. package/app/guide/quickStartGuide.ts +128 -0
  189. package/app/hooks/deleteDbKey.ts +233 -0
  190. package/app/hooks/index.ts +4 -0
  191. package/app/hooks/useAppDetail.ts +208 -0
  192. package/app/hooks/useAppVersions.ts +52 -0
  193. package/app/hooks/useAutoResizeTextarea.ts +29 -0
  194. package/app/hooks/useChatPageTitle.ts +85 -0
  195. package/app/hooks/useClipboardFiles.ts +20 -0
  196. package/app/hooks/useDesktopLocalConnectorAutostart.ts +38 -0
  197. package/app/hooks/useDragResize.ts +91 -0
  198. package/app/hooks/useElementSizeVar.ts +24 -0
  199. package/app/hooks/useFileDropZone.ts +52 -0
  200. package/app/hooks/useHasMounted.ts +11 -0
  201. package/app/hooks/useImageLoadFallback.ts +19 -0
  202. package/app/hooks/useIsMobile.ts +46 -0
  203. package/app/hooks/useMyApps.ts +375 -0
  204. package/app/hooks/useMyContentItems.ts +91 -0
  205. package/app/hooks/useNewMessageNotification.ts +81 -0
  206. package/app/hooks/usePageMeta.ts +107 -0
  207. package/app/hooks/useRecentlyOpened.ts +84 -0
  208. package/app/hooks/useUnifiedStore.ts +11 -0
  209. package/app/hooks/useUserNotifications.ts +197 -0
  210. package/app/hooks.ts +55 -0
  211. package/app/i18n/clientResources.ts +65 -0
  212. package/app/i18n/i18n.base.ts +14 -0
  213. package/app/i18n/i18n.config.ts +62 -0
  214. package/app/i18n/i18n.server.ts +13 -0
  215. package/app/i18n/index.ts +15 -0
  216. package/app/i18n/mapLanguage.ts +11 -0
  217. package/app/i18n/translations/appBuilder.locale.ts +453 -0
  218. package/app/i18n/translations/error.locale.ts +246 -0
  219. package/app/i18n/translations/interface.locale.ts +2174 -0
  220. package/app/i18n/translations/seo.locale.ts +129 -0
  221. package/app/i18n/types.ts +6 -0
  222. package/app/notifications/formatNotification.ts +134 -0
  223. package/app/notifications/model.ts +77 -0
  224. package/app/notifications/notificationSlice.ts +141 -0
  225. package/app/notifications/useNotificationActions.ts +67 -0
  226. package/app/pages/homeTabState.ts +57 -0
  227. package/app/pages/quickChatFlow.ts +109 -0
  228. package/app/pages/rechargeCheckout.ts +23 -0
  229. package/app/pages/usePageArt.ts +91 -0
  230. package/app/realtime/sharedSse.ts +182 -0
  231. package/app/reducer.ts +36 -0
  232. package/app/seo/pageMeta.ts +123 -0
  233. package/app/settings/config.ts +54 -0
  234. package/app/settings/dbActionThunks.ts +34 -0
  235. package/app/settings/defaultSpacePreference.ts +87 -0
  236. package/app/settings/navItems.ts +73 -0
  237. package/app/settings/prepareUserSetting.ts +11 -0
  238. package/app/settings/settingsRecord.ts +8 -0
  239. package/app/settings/web/chat-config/useAutoSaveGlobalPrompt.ts +59 -0
  240. package/app/settings/web/machineStatus.ts +23 -0
  241. package/app/settings/web/useSettingsStylesheet.ts +28 -0
  242. package/app/settings/web/userSecurityUtils.ts +21 -0
  243. package/app/stateViews/runtime.ts +86 -0
  244. package/app/store.ts +53 -0
  245. package/app/theme/colors.ts +280 -0
  246. package/app/theme/fontPreference.ts +77 -0
  247. package/app/theme/index.ts +7 -0
  248. package/app/theme/theme.config.ts +120 -0
  249. package/app/theme/themeModeBootstrap.ts +123 -0
  250. package/app/theme/useSystemTheme.ts +33 -0
  251. package/app/types/appSummary.ts +114 -0
  252. package/app/types.ts +358 -0
  253. package/app/utils/appDeploySource.ts +50 -0
  254. package/app/utils/appKeys.ts +55 -0
  255. package/app/utils/appRuntimeUrl.ts +51 -0
  256. package/app/utils/async.ts +28 -0
  257. package/app/utils/asyncMapStatus.ts +31 -0
  258. package/app/utils/clipboard.ts +86 -0
  259. package/app/utils/credits.ts +12 -0
  260. package/app/utils/desktopAgentRuntimeStatusClient.ts +42 -0
  261. package/app/utils/desktopAgentRuntimeTurnClient.ts +70 -0
  262. package/app/utils/desktopLocalConnectorClient.ts +83 -0
  263. package/app/utils/env.ts +12 -0
  264. package/app/utils/excelToSlate.ts +430 -0
  265. package/app/utils/fileReaders.ts +12 -0
  266. package/app/utils/fileUtils.ts +229 -0
  267. package/app/utils/imageUtils.native.ts +96 -0
  268. package/app/utils/imageUtils.ts +244 -0
  269. package/app/utils/ime.ts +43 -0
  270. package/app/utils/myContentItems.ts +290 -0
  271. package/app/utils/processDateRange.ts +30 -0
  272. package/app/utils/retryFetch.ts +61 -0
  273. package/app/utils/slateToExcel.ts +58 -0
  274. package/app/utils/toast.native.ts +42 -0
  275. package/app/utils/toast.ts +3 -0
  276. package/app/web/spaceInitGuard.ts +28 -0
  277. package/auth/action/signOutAction.ts +0 -0
  278. package/auth/action/signUpAction.ts +227 -0
  279. package/auth/actor.ts +16 -0
  280. package/auth/authSlice.ts +517 -0
  281. package/auth/billing.ts +130 -0
  282. package/auth/client/deleteUserRequest.ts +86 -0
  283. package/auth/client/loginRequest.ts +14 -0
  284. package/auth/hooks/useAuth.ts +9 -0
  285. package/auth/hooks/useDeleteOwnAccount.ts +50 -0
  286. package/auth/hooks/useDeleteOwnAccountFlow.ts +66 -0
  287. package/auth/hooks/useDeleteUser.ts +45 -0
  288. package/auth/hooks/useEnableUser.ts +50 -0
  289. package/auth/hooks/useFetchUsers.ts +77 -0
  290. package/auth/hooks/useRechargeUser.ts +82 -0
  291. package/auth/index.ts +1 -0
  292. package/auth/invite.ts +19 -0
  293. package/auth/locale.ts +84 -0
  294. package/auth/resetAuthScopedClientState.ts +27 -0
  295. package/auth/rn/tokenManager.ts +72 -0
  296. package/auth/routes.ts +127 -0
  297. package/auth/server/accessActivity.ts +143 -0
  298. package/auth/server/billingFullChainSimulator.ts +325 -0
  299. package/auth/server/billingUsageReport.ts +192 -0
  300. package/auth/server/cliLogin.ts +198 -0
  301. package/auth/server/creatorEarningCreditConversion.ts +515 -0
  302. package/auth/server/creatorEarningsLedger.ts +165 -0
  303. package/auth/server/creatorEarningsReport.ts +299 -0
  304. package/auth/server/creatorSettlementLedger.ts +386 -0
  305. package/auth/server/creatorSettlementReport.ts +442 -0
  306. package/auth/server/creatorSplitConfig.ts +191 -0
  307. package/auth/server/deduct.ts +270 -0
  308. package/auth/server/delete.ts +887 -0
  309. package/auth/server/disable.ts +61 -0
  310. package/auth/server/emailAutomation.ts +358 -0
  311. package/auth/server/emailAutomationConfig.ts +88 -0
  312. package/auth/server/emailAutomationRules.ts +94 -0
  313. package/auth/server/emailAutomationTemplates.ts +131 -0
  314. package/auth/server/emailAutomationTypes.ts +38 -0
  315. package/auth/server/emailDbConfig.ts +147 -0
  316. package/auth/server/emailDelivery.ts +960 -0
  317. package/auth/server/emailDeliveryGuards.ts +282 -0
  318. package/auth/server/emailPolicy.ts +43 -0
  319. package/auth/server/emailPreferences.ts +117 -0
  320. package/auth/server/emailProvider.ts +277 -0
  321. package/auth/server/emailReport.ts +439 -0
  322. package/auth/server/emailRuntimeConfig.ts +86 -0
  323. package/auth/server/emailScenarioNotifications.ts +340 -0
  324. package/auth/server/enable.ts +61 -0
  325. package/auth/server/getRechargeHistory.ts +110 -0
  326. package/auth/server/getUser.ts +71 -0
  327. package/auth/server/inviteReward.ts +295 -0
  328. package/auth/server/inviteRewardEmail.ts +1 -0
  329. package/auth/server/ledger.ts +422 -0
  330. package/auth/server/ledgerAudit.ts +424 -0
  331. package/auth/server/ledgerWitness.ts +472 -0
  332. package/auth/server/listusers.ts +154 -0
  333. package/auth/server/locks.ts +32 -0
  334. package/auth/server/login.ts +184 -0
  335. package/auth/server/productBillingSplits.ts +166 -0
  336. package/auth/server/productEntitlements.ts +139 -0
  337. package/auth/server/productPurchase.ts +382 -0
  338. package/auth/server/recharge.ts +284 -0
  339. package/auth/server/revenueShareLedger.ts +389 -0
  340. package/auth/server/revenueShareReport.ts +258 -0
  341. package/auth/server/revenueSplitPolicy.ts +119 -0
  342. package/auth/server/revokeSessions.ts +59 -0
  343. package/auth/server/route.ts +195 -0
  344. package/auth/server/sendEmail.ts +176 -0
  345. package/auth/server/shared.ts +51 -0
  346. package/auth/server/signup.ts +423 -0
  347. package/auth/server/signupIpQuota.ts +44 -0
  348. package/auth/server/token.ts +109 -0
  349. package/auth/server/tokenUsageBilling.ts +312 -0
  350. package/auth/server/transfer.ts +40 -0
  351. package/auth/server/utils.ts +200 -0
  352. package/auth/sessionTokenVersion.ts +38 -0
  353. package/auth/token.ts +63 -0
  354. package/auth/tokenCookie.ts +1 -0
  355. package/auth/types.ts +28 -0
  356. package/auth/utils.ts +108 -0
  357. package/auth/web/bootstrapAuthState.ts +60 -0
  358. package/auth/web/tokenManager.ts +61 -0
  359. package/auth/web/useRegister.ts +70 -0
  360. package/build.ts +16 -0
  361. package/buildPublish.ts +133 -0
  362. package/buildPublishArtifactAssembly.ts +346 -0
  363. package/buildPublishImportAnalysis.ts +237 -0
  364. package/chat/chat.locale.ts +804 -0
  365. package/chat/dialog/actions/addCybotAction.ts +52 -0
  366. package/chat/dialog/actions/addReferenceKeysAction.ts +62 -0
  367. package/chat/dialog/actions/builtinDialogLlm.ts +35 -0
  368. package/chat/dialog/actions/cleanupCliSession.ts +40 -0
  369. package/chat/dialog/actions/compactDialogAndForkAction.ts +47 -0
  370. package/chat/dialog/actions/createDialogAction.ts +372 -0
  371. package/chat/dialog/actions/createScheduledTaskAction.ts +78 -0
  372. package/chat/dialog/actions/handleSendMessageAction.ts +197 -0
  373. package/chat/dialog/actions/handleSendMessageResolver.ts +29 -0
  374. package/chat/dialog/actions/removeCybotAction.ts +6 -0
  375. package/chat/dialog/actions/removeDialogAgentAction.ts +44 -0
  376. package/chat/dialog/actions/replacePrimaryCybotAction.ts +6 -0
  377. package/chat/dialog/actions/setPrimaryDialogAgentAction.ts +52 -0
  378. package/chat/dialog/actions/updateDialogSummaryAction.ts +405 -0
  379. package/chat/dialog/actions/updateDialogTitleAction.ts +151 -0
  380. package/chat/dialog/actions/updateDialogTitlePolicy.ts +30 -0
  381. package/chat/dialog/actions/updateTokensAction.ts +233 -0
  382. package/chat/dialog/dialogAgents.ts +54 -0
  383. package/chat/dialog/dialogPageRenderMode.ts +182 -0
  384. package/chat/dialog/dialogSlice.ts +872 -0
  385. package/chat/dialog/dialogTitle.ts +81 -0
  386. package/chat/dialog/dialogTokenStats.ts +12 -0
  387. package/chat/dialog/dialogUrl.ts +6 -0
  388. package/chat/dialog/ensureDialogSpaceAction.ts +208 -0
  389. package/chat/dialog/goal.ts +108 -0
  390. package/chat/dialog/hooks/useStreamingSymbol.ts +63 -0
  391. package/chat/dialog/objectAssistantRegistry.ts +286 -0
  392. package/chat/dialog/schedule.ts +17 -0
  393. package/chat/dialog/useCreateDialog.ts +52 -0
  394. package/chat/hooks/sendPermissionResolver.ts +128 -0
  395. package/chat/hooks/useChatInput.ts +99 -0
  396. package/chat/hooks/useChatInputSeed.ts +31 -0
  397. package/chat/hooks/useMessageInteraction.ts +412 -0
  398. package/chat/hooks/useSendPermission.ts +92 -0
  399. package/chat/hooks/useThinkingVisibility.ts +27 -0
  400. package/chat/messages/browserImageUrl.ts +48 -0
  401. package/chat/messages/deleteMessages.ts +47 -0
  402. package/chat/messages/fetchAndCacheMessages.ts +119 -0
  403. package/chat/messages/fetchConvMsgs.ts +51 -0
  404. package/chat/messages/fetchMessages.ts +84 -0
  405. package/chat/messages/fileUrl.ts +13 -0
  406. package/chat/messages/generateMessageKey.ts +21 -0
  407. package/chat/messages/hooks/useBase64Migration.ts +115 -0
  408. package/chat/messages/messageAgent.ts +8 -0
  409. package/chat/messages/messageContent.ts +199 -0
  410. package/chat/messages/messageContract.ts +88 -0
  411. package/chat/messages/messageSlice.ts +1191 -0
  412. package/chat/messages/pendingAttachmentParts.ts +117 -0
  413. package/chat/messages/sendFirstMessage.ts +193 -0
  414. package/chat/messages/toolPresentation.ts +212 -0
  415. package/chat/messages/toolThunks.ts +625 -0
  416. package/chat/messages/types.ts +176 -0
  417. package/chat/messages/web/ChatDisplayContext.ts +14 -0
  418. package/chat/messages/web/ToolMessageTypes.ts +23 -0
  419. package/chat/messages/web/diffViewerModel.ts +61 -0
  420. package/chat/messages/web/groupToolEntries.ts +44 -0
  421. package/chat/messages/web/groupToolMessages.ts +35 -0
  422. package/chat/messages/web/inlineReactArtifactParser.ts +63 -0
  423. package/chat/messages/web/toolDisplayName.ts +22 -0
  424. package/chat/messages/web/useLoopStopReason.ts +43 -0
  425. package/chat/messages/ziweiChartDoc.ts +94 -0
  426. package/chat/web/docxToSlate.ts +597 -0
  427. package/chat/web/fileProcessor.ts +442 -0
  428. package/chat/web/hooks/useProgressiveDisplayCount.ts +102 -0
  429. package/chat/web/messageInputAgentUi.ts +189 -0
  430. package/chat/web/messageInputKeyBehavior.ts +53 -0
  431. package/chat/web/messageInputMention.ts +102 -0
  432. package/chat/web/messageInputSendResolver.ts +51 -0
  433. package/chat/web/messageSlashCommands.ts +10 -0
  434. package/chat/web/pdfOcrProcessor.ts +131 -0
  435. package/chat/web/sidebar/allViewSearch.ts +87 -0
  436. package/chat/web/sidebar/types.ts +12 -0
  437. package/chat/web/sidebar/useChatSidebarFilteredData.ts +141 -0
  438. package/chat/web/sidebar/useSidebarDragAndDrop.ts +56 -0
  439. package/cliCommandAdapters.ts +28 -0
  440. package/cliCommandDispatch.ts +66 -0
  441. package/cliCommandFactories.ts +66 -0
  442. package/cliCommandTypes.ts +49 -0
  443. package/cliEnvHelpers.ts +130 -0
  444. package/cliIoTypes.ts +3 -0
  445. package/client/agentRun.test.ts +123 -19
  446. package/client/agentRun.ts +62 -226
  447. package/client/agentRunLocalExecution.test.ts +123 -0
  448. package/client/agentRunLocalExecution.ts +124 -0
  449. package/client/agentRunRuntimeSelection.test.ts +158 -0
  450. package/client/agentRunRuntimeSelection.ts +155 -0
  451. package/client/hybridRecordStore.test.ts +42 -0
  452. package/client/hybridRecordStore.ts +11 -0
  453. package/client/localRuntimeAdapter.test.ts +189 -21
  454. package/client/localRuntimeAdapter.ts +84 -8
  455. package/client/localRuntimeDryRun.test.ts +5 -2
  456. package/client/profileConfig.test.ts +66 -0
  457. package/client/profileConfig.ts +41 -0
  458. package/client/streamingOutput.ts +1 -3
  459. package/client/taskRunPrompt.ts +7 -23
  460. package/commandRegistry.ts +13 -63
  461. package/connector-experimental/capabilities.ts +1 -0
  462. package/connector-experimental/daemonLauncher.ts +189 -0
  463. package/core/builtinAgents.ts +58 -0
  464. package/core/coin.ts +42 -0
  465. package/core/config.ts +15 -0
  466. package/core/crypto.ts +51 -0
  467. package/core/events/types.ts +145 -0
  468. package/core/generateKeyPairFromSeedV1.ts +15 -0
  469. package/core/generateMainKey.ts +22 -0
  470. package/core/init.ts +41 -0
  471. package/core/localOrigins.ts +8 -0
  472. package/core/password.ts +13 -0
  473. package/core/prefix.ts +23 -0
  474. package/create/editor/blockCommands.ts +118 -0
  475. package/create/editor/highlightExample.ts +66 -0
  476. package/create/editor/imageUpload.ts +66 -0
  477. package/create/editor/mark.ts +15 -0
  478. package/create/editor/plugins/normalizeChineseTypography.ts +85 -0
  479. package/create/editor/plugins/withBlockStructure.ts +53 -0
  480. package/create/editor/plugins/withChineseTypography.ts +61 -0
  481. package/create/editor/plugins/withLayout.ts +30 -0
  482. package/create/editor/plugins/withLinks.ts +13 -0
  483. package/create/editor/plugins/withMentions.ts +15 -0
  484. package/create/editor/plugins/withTables.ts +52 -0
  485. package/create/editor/tableCommands.ts +270 -0
  486. package/create/editor/theme/prism/default.ts +145 -0
  487. package/create/editor/theme/prism/githubDark.ts +139 -0
  488. package/create/editor/theme/prism/githubLight.ts +139 -0
  489. package/create/editor/theme/prism/index.ts +23 -0
  490. package/create/editor/theme/prism/okaidia.ts +197 -0
  491. package/create/editor/toolMentionOptions.ts +19 -0
  492. package/create/editor/transforms/fromMarkdown/index.ts +260 -0
  493. package/create/editor/transforms/fromMarkdown/inline.ts +285 -0
  494. package/create/editor/transforms/fromMarkdown/table.ts +130 -0
  495. package/create/editor/transforms/markdownToSlate.ts +24 -0
  496. package/create/editor/transforms/slateToRenderMarkdown.ts +218 -0
  497. package/create/editor/transforms/slateToSimplifiedMarkdown.ts +224 -0
  498. package/create/editor/transforms/slateToText.ts +33 -0
  499. package/create/editor/types.ts +120 -0
  500. package/create/editor/utils/editorFactory.ts +54 -0
  501. package/create/editor/utils/hasPlainCodeBlock.ts +39 -0
  502. package/create/editor/utils/linkCommands.ts +63 -0
  503. package/create/editor/utils/pdfToSlate.ts +706 -0
  504. package/create/editor/utils/slateUtils.ts +233 -0
  505. package/create/editor/utils/txtToSlate.ts +40 -0
  506. package/create/hooks/category.ts +82 -0
  507. package/create/routePaths.ts +5 -0
  508. package/create/space/addSpaceAction.ts +193 -0
  509. package/create/space/category/categoryActions.ts +427 -0
  510. package/create/space/changeSpaceAction.ts +36 -0
  511. package/create/space/constants.ts +8 -0
  512. package/create/space/content/addContentAction.ts +116 -0
  513. package/create/space/content/contentThunks.ts +90 -0
  514. package/create/space/content/deleteContentFromSpaceAction.ts +187 -0
  515. package/create/space/content/deleteMultipleContentAction.ts +112 -0
  516. package/create/space/content/moveContentAction.ts +285 -0
  517. package/create/space/content/updateContentCategoryAction.ts +154 -0
  518. package/create/space/content/updateContentTitleAction.ts +106 -0
  519. package/create/space/content/uploadAndAddFileToSpaceAction.ts +98 -0
  520. package/create/space/contentKeyUtils.ts +117 -0
  521. package/create/space/contentLabels.ts +56 -0
  522. package/create/space/contentTypeMeta.ts +169 -0
  523. package/create/space/deleteSpaceAction.ts +276 -0
  524. package/create/space/fetchSpaceAction.ts +70 -0
  525. package/create/space/fetchSpaceSidebarStateAction.ts +26 -0
  526. package/create/space/hooks/agentFetchRetry.ts +19 -0
  527. package/create/space/hooks/useAgentFetcher.ts +149 -0
  528. package/create/space/hooks/useGroupedContent.ts +118 -0
  529. package/create/space/hooks/useSpaceEvents.ts +43 -0
  530. package/create/space/initialSpace.ts +78 -0
  531. package/create/space/loadDefaultSpaceAction.ts +57 -0
  532. package/create/space/member/addMemberAction.ts +79 -0
  533. package/create/space/member/fetchUserSpaceMembershipsAction.ts +386 -0
  534. package/create/space/member/memberThunks.ts +47 -0
  535. package/create/space/member/removeMemberAction.ts +67 -0
  536. package/create/space/publicCatalogSpace.ts +39 -0
  537. package/create/space/resolvePreferredSpaceId.ts +113 -0
  538. package/create/space/sidebarVisibleTypes.ts +214 -0
  539. package/create/space/space.locale.ts +981 -0
  540. package/create/space/spaceAccess.ts +182 -0
  541. package/create/space/spaceCollapsedState.ts +54 -0
  542. package/create/space/spaceKeys.ts +65 -0
  543. package/create/space/spaceSlice.ts +328 -0
  544. package/create/space/spaceThunks.ts +410 -0
  545. package/create/space/types.ts +27 -0
  546. package/create/space/updateSpaceAction.ts +107 -0
  547. package/create/space/utils/permissions.ts +47 -0
  548. package/create/space/validateSpacePermission.ts +38 -0
  549. package/create/types.ts +21 -0
  550. package/create/version/appVersionReplication.ts +160 -0
  551. package/create/version/versionApi.ts +142 -0
  552. package/customCodingAgentFlow.ts +208 -0
  553. package/customCodingAgentRecord.ts +179 -0
  554. package/database/actions/agentReadResolution.ts +33 -0
  555. package/database/actions/cacheMergedUserData.ts +1 -1
  556. package/database/actions/common.ts +44 -8
  557. package/database/actions/fetchUserData.ts +1 -1
  558. package/database/actions/fileContent.ts +3 -3
  559. package/database/actions/patch.ts +3 -3
  560. package/database/actions/read.ts +115 -76
  561. package/database/actions/readAndWait.ts +54 -86
  562. package/database/actions/readOutcome.ts +45 -0
  563. package/database/actions/readResolution.ts +102 -0
  564. package/database/actions/remove.ts +2 -2
  565. package/database/actions/upload.ts +4 -4
  566. package/database/actions/upsert.ts +2 -2
  567. package/database/actions/write.ts +4 -4
  568. package/database/client/handleError.ts +1 -1
  569. package/database/client/queryRequest.ts +1 -1
  570. package/database/dbSlice.ts +2 -2
  571. package/database/email.ts +1 -1
  572. package/database/hooks/useUserData.ts +4 -4
  573. package/database/keys.ts +2 -2
  574. package/database/runtimeServerContext.ts +3 -3
  575. package/database/server/actorAccess.ts +1 -1
  576. package/database/server/coreDataProxy.ts +43 -26
  577. package/database/server/cybotReadonly.ts +1 -1
  578. package/database/server/dataHandlers.ts +1 -1
  579. package/database/server/delete.ts +8 -8
  580. package/database/server/emailRepository.ts +7 -7
  581. package/database/server/fileService.ts +1 -1
  582. package/database/server/handleTransaction.ts +2 -2
  583. package/database/server/patch.ts +3 -3
  584. package/database/server/read.ts +5 -5
  585. package/database/server/resourceAccess.ts +2 -2
  586. package/database/server/routes.ts +3 -3
  587. package/database/server/write.ts +5 -5
  588. package/database/table/deleteTable.ts +1 -1
  589. package/database/userPreferenceRegister.ts +2 -2
  590. package/dialogDataAccess.ts +186 -0
  591. package/dialogInternalCommandEntries.ts +11 -0
  592. package/dialogReadCommands.ts +179 -0
  593. package/dialogReadHelpers.ts +226 -0
  594. package/dialogStatusCommands.ts +122 -0
  595. package/docInternalCommandEntries.ts +20 -0
  596. package/hybridReadHelpers.ts +109 -0
  597. package/index.ts +56 -197
  598. package/integrations/anthropic/anthropicModels.ts +47 -0
  599. package/integrations/anthropic/type.ts +41 -0
  600. package/integrations/deepseek/models.ts +67 -0
  601. package/integrations/google/models.ts +298 -0
  602. package/integrations/openai/chatCompletionStreamMode.ts +10 -0
  603. package/integrations/openai/filterAndCleanMessages.ts +284 -0
  604. package/integrations/openai/flexTier.ts +102 -0
  605. package/integrations/openai/generateOpenAIRequestBody.ts +274 -0
  606. package/integrations/openai/generateResponseRequestBody.ts +77 -0
  607. package/integrations/openai/models.ts +271 -0
  608. package/integrations/openai/providerBodyCompatibility.ts +23 -0
  609. package/integrations/openai/responsesHelpers.ts +297 -0
  610. package/integrations/postmark/index.ts +57 -0
  611. package/integrations/resend/index.ts +353 -0
  612. package/integrations/weather/getWeather.ts +24 -0
  613. package/integrations/weather/index.ts +6 -0
  614. package/integrations/weather/routes.ts +31 -0
  615. package/integrations/weather/weatherApi.ts +27 -0
  616. package/integrations/weather/weatherConstants.ts +2 -0
  617. package/integrations/weather/weatherI18n.ts +50 -0
  618. package/integrations/weather/weatherService.ts +50 -0
  619. package/integrations/weather/weatherTypes.ts +140 -0
  620. package/integrations/weather/weatherUtils.ts +46 -0
  621. package/integrations/x-reader/backends/localBrowser.ts +215 -0
  622. package/integrations/x-reader/backends/publicFallback.ts +31 -0
  623. package/integrations/x-reader/backends/rawCdp.ts +218 -0
  624. package/integrations/x-reader/bridge/chromeBridgeManager.ts +235 -0
  625. package/integrations/x-reader/bridge/readXPostWithBridge.ts +73 -0
  626. package/integrations/x-reader/fixtureParser.ts +101 -0
  627. package/integrations/x-reader/index.ts +9 -0
  628. package/integrations/x-reader/redaction.ts +27 -0
  629. package/integrations/x-reader/types.ts +109 -0
  630. package/integrations/x-reader/visibleTextParser.ts +108 -0
  631. package/integrations/xai/models.ts +13 -0
  632. package/internalCommandEntries.ts +20 -0
  633. package/machineCommandPrompts.ts +77 -0
  634. package/machineCommandResolution.ts +43 -0
  635. package/machineCommands.ts +104 -853
  636. package/machineDaemonCommands.ts +29 -0
  637. package/machineHeartbeatCommands.ts +29 -0
  638. package/machineStatusCommands.ts +95 -0
  639. package/machineWatchCommands.ts +41 -0
  640. package/machineWsPromptMaterialization.ts +89 -0
  641. package/machineWsRunDispatch.ts +570 -0
  642. package/machineWsSession.ts +148 -0
  643. package/package.json +126 -4
  644. package/pageCreateCommands.ts +219 -0
  645. package/pageDeleteCommands.ts +167 -0
  646. package/pageReadCommands.ts +151 -0
  647. package/pageUpdateCommands.ts +249 -0
  648. package/pageWriteHelpers.ts +209 -0
  649. package/publishPackage.ts +36 -0
  650. package/render/canvas/canvasEditContext.ts +1 -1
  651. package/render/layout/mainLayoutViewMode.ts +1 -1
  652. package/render/page/createPageAction.ts +12 -12
  653. package/render/page/docSlice.ts +8 -8
  654. package/render/page/server/createPage.ts +7 -7
  655. package/render/page/server/handleCreatePage.ts +2 -2
  656. package/render/page/types.ts +2 -2
  657. package/render/surf/utils/groupedWeatherData.ts +1 -1
  658. package/render/table/createTableAction.ts +7 -7
  659. package/render/table/deleteTableAction.ts +4 -4
  660. package/render/table/fetchAndCacheTableRows.ts +2 -2
  661. package/render/table/tableSlice.ts +6 -6
  662. package/render/table/types.ts +1 -1
  663. package/render/table/useCreateTable.ts +2 -2
  664. package/render/table/useTable.ts +2 -2
  665. package/render/table/utils/tableSerialization.ts +1 -1
  666. package/runtimeDoctorCommands.ts +118 -0
  667. package/runtimeInternalCommandEntries.ts +15 -0
  668. package/runtimePassthroughCommands.ts +22 -0
  669. package/scriptCommandEntries.ts +31 -0
  670. package/scriptPassthroughHelpers.ts +28 -0
  671. package/secretScanHelpers.ts +31 -0
  672. package/server/agentEmailRegistrationRoutes.ts +29 -0
  673. package/server/agentRunRouteContract.ts +9 -0
  674. package/server/agentRunRoutes.ts +10 -0
  675. package/server/api/methods.ts +333 -0
  676. package/server/api/tableHandlers.ts +55 -0
  677. package/server/api/types.ts +27 -0
  678. package/server/apiContract.ts +88 -0
  679. package/server/apiRouteAdapter.ts +218 -0
  680. package/server/appBundler.ts +90 -0
  681. package/server/appRoutes.ts +92 -0
  682. package/server/artifactIconsRoute.ts +89 -0
  683. package/server/artifactRuntimeRoute.ts +136 -0
  684. package/server/authenticateToken.ts +73 -0
  685. package/server/billingLedgerRoutes.ts +48 -0
  686. package/server/cliChatRoutes.ts +5 -0
  687. package/server/cloudflareRoutes.ts +35 -0
  688. package/server/coreHeaders.ts +29 -0
  689. package/server/coreReadiness.ts +39 -0
  690. package/server/coreRoutes.ts +65 -0
  691. package/server/coreRuntime.ts +93 -0
  692. package/server/createResponse.ts +68 -0
  693. package/server/cryptoPaymentRoutes.ts +8 -0
  694. package/server/databaseRoutes.ts +225 -0
  695. package/server/desktopRuntimeRoutes.ts +120 -0
  696. package/server/devBuildInfo.ts +51 -0
  697. package/server/devReload.ts +161 -0
  698. package/server/devToolPathPolicy.ts +59 -0
  699. package/server/devToolRoutes.ts +274 -0
  700. package/server/email/cloudflareEmailProvider.ts +119 -0
  701. package/server/email/cloudflareInboundBridge.ts +121 -0
  702. package/server/email/cloudflareInboundWorker.ts +28 -0
  703. package/server/email/provider.ts +149 -0
  704. package/server/email/providerRegistry.ts +22 -0
  705. package/server/emailInboundRoutes.ts +17 -0
  706. package/server/entry.ts +211 -0
  707. package/server/handleRPCRequest.ts +117 -0
  708. package/server/handlers/activityStatsHandler.ts +74 -0
  709. package/server/handlers/agentEmailRegistrationE2EHandler.ts +406 -0
  710. package/server/handlers/agentEmailRegistrationE2ERoutes.ts +6 -0
  711. package/server/handlers/agentRun/agentLookup.ts +77 -0
  712. package/server/handlers/agentRun/autonomousRuntime.ts +355 -0
  713. package/server/handlers/agentRun/backgroundRunDialog.ts +31 -0
  714. package/server/handlers/agentRun/builtinAgents.ts +347 -0
  715. package/server/handlers/agentRun/continueDialogContext.ts +90 -0
  716. package/server/handlers/agentRun/debugContext.ts +58 -0
  717. package/server/handlers/agentRun/dialogLookup.ts +67 -0
  718. package/server/handlers/agentRun/foregroundStreamDialog.ts +13 -0
  719. package/server/handlers/agentRun/index.ts +1630 -0
  720. package/server/handlers/agentRun/inheritedDialogPrompt.ts +111 -0
  721. package/server/handlers/agentRun/localRuntimeBootstrap.ts +69 -0
  722. package/server/handlers/agentRun/loop.ts +2210 -0
  723. package/server/handlers/agentRun/machineConnector.ts +235 -0
  724. package/server/handlers/agentRun/machineRunAudit.ts +49 -0
  725. package/server/handlers/agentRun/modelUsageToolHandlers.ts +368 -0
  726. package/server/handlers/agentRun/referenceRuntime.ts +50 -0
  727. package/server/handlers/agentRun/requestContext.ts +86 -0
  728. package/server/handlers/agentRun/response.ts +27 -0
  729. package/server/handlers/agentRun/runBilling.ts +108 -0
  730. package/server/handlers/agentRun/runDialogPreparation.ts +55 -0
  731. package/server/handlers/agentRun/runtimeBinding.ts +61 -0
  732. package/server/handlers/agentRun/runtimeContext.ts +143 -0
  733. package/server/handlers/agentRun/runtimeExecutionContext.ts +153 -0
  734. package/server/handlers/agentRun/runtimePreparation.ts +193 -0
  735. package/server/handlers/agentRun/runtimeRouting.ts +88 -0
  736. package/server/handlers/agentRun/runtimeSystemMessages.ts +165 -0
  737. package/server/handlers/agentRun/saveDialog.ts +1373 -0
  738. package/server/handlers/agentRun/syncRunDialog.ts +90 -0
  739. package/server/handlers/agentRun/tableActivityProjection.ts +132 -0
  740. package/server/handlers/agentRun/taskRunControlPrompt.ts +122 -0
  741. package/server/handlers/agentRun/taskRunToolHandler.ts +579 -0
  742. package/server/handlers/agentRun/toolExecutor.ts +2909 -0
  743. package/server/handlers/agentRun/toolPolicyGuards.ts +295 -0
  744. package/server/handlers/agentRun/types.ts +94 -0
  745. package/server/handlers/agentRun/workspaceLease.ts +58 -0
  746. package/server/handlers/alphaTestRouteGuard.ts +30 -0
  747. package/server/handlers/apifyActorHandler.ts +170 -0
  748. package/server/handlers/appDeployFlow.ts +876 -0
  749. package/server/handlers/appDeployHandler.ts +4 -0
  750. package/server/handlers/appDeployMutationHandlers.ts +133 -0
  751. package/server/handlers/appDeployReadHandlers.ts +321 -0
  752. package/server/handlers/appDeployShared.ts +230 -0
  753. package/server/handlers/appDeployStatusStore.ts +178 -0
  754. package/server/handlers/appDomainHandler.ts +476 -0
  755. package/server/handlers/appLegacyInfraCleanup.ts +7 -0
  756. package/server/handlers/appSandboxHandler.ts +1055 -0
  757. package/server/handlers/appSourceReadiness.ts +75 -0
  758. package/server/handlers/appSpaceAccess.ts +179 -0
  759. package/server/handlers/applyEditHandler.ts +544 -0
  760. package/server/handlers/applyLineEditsHandler.ts +1058 -0
  761. package/server/handlers/billingLedgerAuditHandler.ts +82 -0
  762. package/server/handlers/billingLedgerWitnessHandler.ts +161 -0
  763. package/server/handlers/billingUsageReportHandler.ts +90 -0
  764. package/server/handlers/browserToolHandler.ts +450 -0
  765. package/server/handlers/cfTextToSpeechHandler.ts +137 -0
  766. package/server/handlers/cfWhisperHandler.ts +206 -0
  767. package/server/handlers/chatBillingSse.ts +79 -0
  768. package/server/handlers/chatHandler.ts +797 -0
  769. package/server/handlers/chatProxyRouting.ts +404 -0
  770. package/server/handlers/chatUpstreamRetry.ts +162 -0
  771. package/server/handlers/checkEnvHandler.ts +250 -0
  772. package/server/handlers/cliChatHandler.ts +266 -0
  773. package/server/handlers/cloudflareBrowserHandler.ts +275 -0
  774. package/server/handlers/cloudflareCrawlHandler.ts +260 -0
  775. package/server/handlers/codeSearchHandler.ts +484 -0
  776. package/server/handlers/connector/connectorConnectionRegistry.ts +192 -0
  777. package/server/handlers/connector/connectorWebSocket.ts +117 -0
  778. package/server/handlers/creatorEarningsReportHandler.ts +97 -0
  779. package/server/handlers/creatorSettlementReportHandler.ts +98 -0
  780. package/server/handlers/deepseekOcrHandler.ts +114 -0
  781. package/server/handlers/deleteMemoryHandler.ts +115 -0
  782. package/server/handlers/desktopAgentRuntimeAdapter.ts +307 -0
  783. package/server/handlers/desktopAgentRuntimeHostFacts.ts +40 -0
  784. package/server/handlers/desktopAgentRuntimeStatusHandler.ts +54 -0
  785. package/server/handlers/desktopAgentRuntimeTurnHandler.ts +165 -0
  786. package/server/handlers/desktopAgentRuntimeTurnService.ts +352 -0
  787. package/server/handlers/desktopClipboardHandler.ts +104 -0
  788. package/server/handlers/desktopFiles/handler.ts +159 -0
  789. package/server/handlers/desktopFiles/operations.ts +202 -0
  790. package/server/handlers/desktopFiles/sandbox.ts +62 -0
  791. package/server/handlers/desktopFiles/store.ts +81 -0
  792. package/server/handlers/desktopLlamaRuntimeHandler.ts +360 -0
  793. package/server/handlers/desktopLocalConnectorHandler.ts +101 -0
  794. package/server/handlers/desktopUpdaterHandler.ts +132 -0
  795. package/server/handlers/dialogEvents.ts +10 -0
  796. package/server/handlers/emailInboundHandler.ts +65 -0
  797. package/server/handlers/eventBus.ts +160 -0
  798. package/server/handlers/exaSearchHandler.ts +73 -0
  799. package/server/handlers/execShellHandler.ts +661 -0
  800. package/server/handlers/favoriteHandlers.ts +417 -0
  801. package/server/handlers/fetchWebpageHandler.ts +117 -0
  802. package/server/handlers/fileOutlineHandler.ts +107 -0
  803. package/server/handlers/geminiImagePreviewHandler.ts +612 -0
  804. package/server/handlers/getDialogBillingHandler.ts +151 -0
  805. package/server/handlers/getTransactionsHandler.ts +107 -0
  806. package/server/handlers/googleNativeChat.ts +572 -0
  807. package/server/handlers/listFilesHandler.ts +139 -0
  808. package/server/handlers/machines/machineAuth.ts +149 -0
  809. package/server/handlers/machines/machineBinding.ts +59 -0
  810. package/server/handlers/machines/machineHandlers.ts +279 -0
  811. package/server/handlers/machines/machineInstallAssets.ts +563 -0
  812. package/server/handlers/machines/machineRegistry.ts +89 -0
  813. package/server/handlers/notificationEmitter.ts +189 -0
  814. package/server/handlers/notificationsHandler.ts +91 -0
  815. package/server/handlers/olmOcrHandler.ts +115 -0
  816. package/server/handlers/openaiImageHandler.ts +500 -0
  817. package/server/handlers/providerHealthHandler.ts +36 -0
  818. package/server/handlers/publicHandler.ts +209 -0
  819. package/server/handlers/readFileHandler.ts +300 -0
  820. package/server/handlers/readXPostHandler.ts +180 -0
  821. package/server/handlers/rememberMemoryHandler.ts +69 -0
  822. package/server/handlers/remotionRenderHandler.ts +236 -0
  823. package/server/handlers/renderMarkdownHandler.ts +66 -0
  824. package/server/handlers/revenueShareReportHandler.ts +96 -0
  825. package/server/handlers/searchRepoHandler.ts +159 -0
  826. package/server/handlers/tableAccess.ts +34 -0
  827. package/server/handlers/tableToolHandlers.ts +741 -0
  828. package/server/handlers/typecheckWorker.ts +54 -0
  829. package/server/handlers/userEvents.ts +6 -0
  830. package/server/handlers/userSecretsHandler.ts +210 -0
  831. package/server/handlers/versionHandler.ts +281 -0
  832. package/server/handlers/versionStore.ts +176 -0
  833. package/server/handlers/wereadGatewayHandler.ts +48 -0
  834. package/server/handlers/whisperHandler.ts +158 -0
  835. package/server/handlers/writeFileHandler.ts +168 -0
  836. package/server/html/desktopReleaseManifest.server.ts +17 -0
  837. package/server/html/generateErrorResponse.ts +22 -0
  838. package/server/html/serializeState.ts +9 -0
  839. package/server/html/ssrPreload.ts +247 -0
  840. package/server/html/template.ts +275 -0
  841. package/server/httpCompression.ts +102 -0
  842. package/server/index.ts +0 -0
  843. package/server/liveReloadScript.ts +105 -0
  844. package/server/luIconNames.ts +14 -0
  845. package/server/machineRoutes.ts +44 -0
  846. package/server/mediaRoutes.ts +25 -0
  847. package/server/memoryRoutes.ts +14 -0
  848. package/server/normalizedStatusRoutes.ts +23 -0
  849. package/server/payments/crypto/cryptoUsdcBaseAddressPool.ts +49 -0
  850. package/server/payments/crypto/cryptoUsdcBaseConfig.ts +120 -0
  851. package/server/payments/crypto/cryptoUsdcBasePaymentProvider.ts +134 -0
  852. package/server/payments/crypto/cryptoUsdcBaseScanner.ts +267 -0
  853. package/server/payments/crypto/cryptoUsdcBaseWatcher.ts +40 -0
  854. package/server/payments/crypto/cryptoUsdtTronAddressPool.ts +49 -0
  855. package/server/payments/crypto/cryptoUsdtTronConfig.ts +120 -0
  856. package/server/payments/crypto/cryptoUsdtTronPaymentProvider.ts +132 -0
  857. package/server/payments/crypto/cryptoUsdtTronScanner.ts +313 -0
  858. package/server/payments/crypto/cryptoUsdtTronWatcher.ts +40 -0
  859. package/server/payments/paymentProvider.ts +6 -0
  860. package/server/payments/paymentRoutes.ts +27 -0
  861. package/server/payments/rechargeCredits.ts +69 -0
  862. package/server/payments/waffo/waffoPaymentProvider.ts +341 -0
  863. package/server/preview/previewCapability.ts +90 -0
  864. package/server/preview/previewConfig.ts +54 -0
  865. package/server/preview/previewGateway.ts +71 -0
  866. package/server/preview/previewRealtime.ts +33 -0
  867. package/server/providerHealthProbe.ts +198 -0
  868. package/server/providerHealthRoutes.ts +19 -0
  869. package/server/publicPaths.ts +12 -0
  870. package/server/publicRequestHandler.ts +15 -0
  871. package/server/reactAppBuilder.ts +416 -0
  872. package/server/realtimeRoutes.ts +69 -0
  873. package/server/renderStream.ts +20 -0
  874. package/server/routeFactory.ts +45 -0
  875. package/server/routes.ts +125 -0
  876. package/server/safeTempFile.ts +48 -0
  877. package/server/scheduler/scheduledTaskRuntime.ts +228 -0
  878. package/server/scheduler/schedulerService.ts +290 -0
  879. package/server/serverDrain.ts +129 -0
  880. package/server/services/browserSessionManager.ts +354 -0
  881. package/server/shareRoutes.ts +86 -0
  882. package/server/sqliteRoutes.ts +141 -0
  883. package/server/taskRunBridgeRoutes.ts +95 -0
  884. package/server/tasks.ts +113 -0
  885. package/server/waffoPaymentRoutes.ts +5 -0
  886. package/server/webUtilityRoutes.ts +50 -0
  887. package/server/workerValidator.ts +224 -0
  888. package/share/action.ts +225 -0
  889. package/share/clientTableReplication.ts +160 -0
  890. package/share/helpers.ts +302 -0
  891. package/share/keys.ts +243 -0
  892. package/share/link.ts +40 -0
  893. package/share/loadOwnerShares.ts +154 -0
  894. package/share/paidAccess.ts +32 -0
  895. package/share/publicSharePolicy.ts +27 -0
  896. package/share/server/fetchCommunityShares.ts +45 -0
  897. package/share/server/helpers.ts +202 -0
  898. package/share/server/importShare.ts +148 -0
  899. package/share/server/listCommunityShares.ts +167 -0
  900. package/share/server/purchaseDoc.ts +224 -0
  901. package/share/server/readCommunityShareCover.ts +73 -0
  902. package/share/server/readSharedTable.ts +139 -0
  903. package/share/server/tableReplication.ts +351 -0
  904. package/share/shareSlice.ts +53 -0
  905. package/share/tableShareState.ts +74 -0
  906. package/share/types.ts +100 -0
  907. package/spaceCategoryCommands.ts +263 -0
  908. package/spaceCommands.ts +295 -0
  909. package/spaceDeleteCommands.ts +312 -0
  910. package/spaceInternalCommandEntries.ts +19 -0
  911. package/spaceMutationHelpers.ts +135 -0
  912. package/spaceReadCommands.ts +323 -0
  913. package/systemInternalCommandEntries.ts +65 -0
  914. package/tableCommands.ts +33 -133
  915. package/tableDataOutput.ts +41 -0
  916. package/tableDataSafety.ts +26 -0
  917. package/tableMetaCommands.ts +307 -0
  918. package/tableMutationCommands.ts +266 -0
  919. package/tableQueryParsing.ts +90 -0
  920. package/tableQueryRequest.ts +53 -0
  921. package/taskRunActions.ts +122 -0
  922. package/taskRunCommand.ts +8 -214
  923. package/taskRunParsing.ts +86 -0
  924. package/taskRunPromptShared.ts +55 -0
  925. package/tui/session.ts +43 -372
  926. package/tui/tuiCatalog.ts +22 -0
  927. package/tui/tuiCommandInterpreter.test.ts +169 -0
  928. package/tui/tuiCommandInterpreter.ts +253 -0
  929. package/tui/tuiPresentation.test.ts +154 -0
  930. package/tui/tuiPresentation.ts +146 -0
  931. package/workflowInternalCommandEntries.ts +31 -0
@@ -0,0 +1,2210 @@
1
+ // packages/server/handlers/agentRun/loop.ts
2
+ //
3
+ // 纯函数 Agent Loop(无 Redux,无持久化)。
4
+ // - platform/custom: 直接调用 LLM API,支持本地 tool_calls 循环
5
+ // - cli: 复用 prompt / model / 最近文本历史,但不走本地 tool_calls 协议
6
+ // - 调用方负责传入 messages 历史并保存返回结果
7
+
8
+ import { buildCliPrompt, executeCli, executeCliStreaming, type CliProvider } from "../../../ai/agent/cliExecutor";
9
+ import { findModelConfig, getApiEndpoint } from "../../../ai/llm/providers";
10
+ import {
11
+ DEEPINFRA_KIMI_FALLBACK_MODEL,
12
+ FIREWORKS_KIMI_CURRENT_MODEL,
13
+ KIMI_PLATFORM_FALLBACK_STATUSES,
14
+ OPENROUTER_KIMI_FALLBACK_MODEL,
15
+ isDeepInfraKimiModel,
16
+ isFireworksKimiModel,
17
+ } from "../../../ai/llm/kimi";
18
+ import { getModelInfo } from "../../../ai/llm/getModelContextWindow";
19
+ import { isResponseAPIModel } from "../../../ai/llm/isResponseAPIModel";
20
+ import { getNoloKey } from "../../../ai/llm/getNoloKey";
21
+ import { prepareTools } from "../../../ai/tools/prepareTools";
22
+ import { canonicalizeToolName } from "../../../ai/tools/toolNameAliases";
23
+ import { generateRequestBody } from "../../../ai/llm/generateRequestBody";
24
+ import {
25
+ fetchGoogleNativeImageCompletion,
26
+ fetchGoogleNativeImageChat,
27
+ isGoogleNativeImageModel,
28
+ } from "../googleNativeChat";
29
+ import {
30
+ extractImagePartsFromResponseOutput,
31
+ extractTextFromResponseOutput,
32
+ extractToolCallsFromResponseOutput,
33
+ toResponsesTools,
34
+ } from "../../../integrations/openai/responsesHelpers";
35
+ import { normalizeChatCompletionsBodyForProvider } from "../../../integrations/openai/providerBodyCompatibility";
36
+ import { fetchUpstreamWithGentleRetry } from "../chatUpstreamRetry";
37
+ import { executeToolOnServer } from "./toolExecutor";
38
+ import type {
39
+ AgentRunRuntimeContext,
40
+ ChatMessage,
41
+ AssistantToolCall,
42
+ LoopResult,
43
+ ToolPolicyRuntimeState,
44
+ } from "./types";
45
+ import type { ResolvedRuntimePolicy } from "../../../ai/policy/runtimePolicy";
46
+ import { isLocalEndpoint } from "./localRuntimeBootstrap";
47
+
48
+ const MAX_TIME_MS = 10 * 60_000; // 10 min
49
+ const DEFAULT_LLM_REQUEST_TIMEOUT_MS = 300_000;
50
+ const DEFAULT_LOCAL_LLM_REQUEST_TIMEOUT_MS = MAX_TIME_MS;
51
+ const OPENROUTER_CHAT_COMPLETIONS_URL =
52
+ "https://openrouter.ai/api/v1/chat/completions";
53
+ const GOOGLE_FALLBACK_STATUSES = [404, 429, 500, 502, 503, 504];
54
+ const FIREWORKS_OPENROUTER_FALLBACK_STATUSES = KIMI_PLATFORM_FALLBACK_STATUSES;
55
+ const BUILTIN_NOLO_AGENT_KEY = "agent-pub-01NOLOAPPBLD000000019KCKT0";
56
+
57
+ const toOpenRouterGoogleModelName = (modelName: string) => `google/${modelName}`;
58
+
59
+ const parseTimeoutOverride = (rawValue: string | undefined): number | null => {
60
+ const parsed = Number(rawValue);
61
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
62
+ };
63
+
64
+ export const resolveLlmRequestTimeoutMs = (
65
+ agentConfig: any,
66
+ endpoint: string,
67
+ ): number => {
68
+ const globalOverride = parseTimeoutOverride(process.env.AGENT_LOOP_LLM_TIMEOUT_MS);
69
+ const localOverride = parseTimeoutOverride(process.env.AGENT_LOOP_LOCAL_LLM_TIMEOUT_MS);
70
+ const isLocalCustomProvider =
71
+ !!agentConfig?.customProviderUrl && isLocalEndpoint(endpoint);
72
+
73
+ if (isLocalCustomProvider && localOverride) {
74
+ return localOverride;
75
+ }
76
+ if (globalOverride) {
77
+ return globalOverride;
78
+ }
79
+ return isLocalCustomProvider
80
+ ? DEFAULT_LOCAL_LLM_REQUEST_TIMEOUT_MS
81
+ : DEFAULT_LLM_REQUEST_TIMEOUT_MS;
82
+ };
83
+
84
+ function buildUpstreamHeaders(
85
+ requestUrl: string,
86
+ apiKey?: string | null,
87
+ apiKeyHeader?: string | null,
88
+ ) {
89
+ const headers: Record<string, string> = {
90
+ "Content-Type": "application/json",
91
+ };
92
+ const normalizedApiKeyHeader =
93
+ apiKeyHeader?.trim() ||
94
+ (/xiaomimimo\.com/i.test(requestUrl) ? "api-key" : "");
95
+ if (apiKey) {
96
+ if (normalizedApiKeyHeader) {
97
+ headers[normalizedApiKeyHeader] = apiKey;
98
+ } else {
99
+ headers.Authorization = `Bearer ${apiKey}`;
100
+ }
101
+ }
102
+
103
+ if (requestUrl.includes("openrouter.ai")) {
104
+ headers["HTTP-Referer"] = "https://nolo.chat";
105
+ headers["X-Title"] = "nolo";
106
+ }
107
+
108
+ return headers;
109
+ }
110
+
111
+ async function readAgentApiKeyReference(args: {
112
+ agentKey: string;
113
+ serverBase: string;
114
+ authToken: string;
115
+ }): Promise<string | undefined> {
116
+ const trimmedAgentKey = args.agentKey.trim();
117
+ if (!trimmedAgentKey) return undefined;
118
+
119
+ const url = `${args.serverBase.replace(/\/+$/, "")}/api/v1/db/read/${encodeURIComponent(trimmedAgentKey)}`;
120
+ const headers: Record<string, string> = {};
121
+ if (args.authToken) {
122
+ headers.Authorization = `Bearer ${args.authToken}`;
123
+ }
124
+
125
+ const response = await fetch(url, {
126
+ method: "GET",
127
+ headers,
128
+ signal: AbortSignal.timeout(30_000),
129
+ });
130
+ if (!response.ok) {
131
+ throw new Error(`Failed to read apiKeyFromAgentKey agent (${response.status}).`);
132
+ }
133
+
134
+ const payload = await response.json() as any;
135
+ const record = payload?.data ?? payload;
136
+ const key = typeof record?.apiKey === "string" ? record.apiKey.trim() : "";
137
+ return key || undefined;
138
+ }
139
+
140
+ async function hydrateApiKeyReferenceIfNeeded(
141
+ agentConfig: any,
142
+ serverBase: string,
143
+ authToken: string,
144
+ ) {
145
+ if (agentConfig?.apiKey?.trim()) return agentConfig;
146
+ const sourceAgentKey =
147
+ typeof agentConfig?.apiKeyFromAgentKey === "string"
148
+ ? agentConfig.apiKeyFromAgentKey.trim()
149
+ : "";
150
+ if (!sourceAgentKey) return agentConfig;
151
+
152
+ const apiKey = await readAgentApiKeyReference({
153
+ agentKey: sourceAgentKey,
154
+ serverBase,
155
+ authToken,
156
+ });
157
+ if (!apiKey) {
158
+ throw new Error(`apiKeyFromAgentKey did not resolve an API key for ${sourceAgentKey}.`);
159
+ }
160
+
161
+ return {
162
+ ...agentConfig,
163
+ apiKey,
164
+ };
165
+ }
166
+
167
+ async function fetchLoopUpstream(
168
+ endpoint: string,
169
+ agentConfig: any,
170
+ body: Record<string, any>,
171
+ ): Promise<{
172
+ response: Response;
173
+ provider?: string;
174
+ model?: string;
175
+ inputPrice?: number;
176
+ outputPrice?: number;
177
+ }> {
178
+ const apiKey = getApiKey(agentConfig);
179
+ const llmRequestTimeoutMs = resolveLlmRequestTimeoutMs(agentConfig, endpoint);
180
+ const init: RequestInit = {
181
+ method: "POST",
182
+ headers: {
183
+ ...buildUpstreamHeaders(endpoint, apiKey, agentConfig?.apiKeyHeader),
184
+ },
185
+ body: JSON.stringify(body),
186
+ signal: AbortSignal.timeout(llmRequestTimeoutMs),
187
+ };
188
+
189
+ const shouldUseGoogleOpenRouterFallback =
190
+ String(agentConfig?.provider || "").toLowerCase() === "google" &&
191
+ typeof agentConfig?.model === "string" &&
192
+ !!agentConfig.model &&
193
+ !agentConfig?.customProviderUrl;
194
+ const openRouterApiKey = shouldUseGoogleOpenRouterFallback
195
+ ? getNoloKey("openrouter")
196
+ : null;
197
+ const shouldUseConfiguredFallback =
198
+ String(agentConfig?.provider || "").toLowerCase() === "fireworks" &&
199
+ typeof agentConfig?.fallbackProvider === "string" &&
200
+ agentConfig.fallbackProvider.trim().toLowerCase() === "openrouter" &&
201
+ typeof agentConfig?.fallbackModel === "string" &&
202
+ !!agentConfig.fallbackModel.trim() &&
203
+ !agentConfig?.customProviderUrl;
204
+ const normalizedAgentProvider = String(agentConfig?.provider || "").toLowerCase();
205
+ const shouldUseKimiPlatformFallback =
206
+ ((normalizedAgentProvider === "deepinfra" &&
207
+ isDeepInfraKimiModel(agentConfig?.model)) ||
208
+ (normalizedAgentProvider === "fireworks" &&
209
+ isFireworksKimiModel(agentConfig?.model))) &&
210
+ !agentConfig?.customProviderUrl;
211
+ const fireworksKimiFallbackApiKey =
212
+ shouldUseKimiPlatformFallback && normalizedAgentProvider !== "fireworks"
213
+ ? getNoloKey("fireworks")
214
+ : null;
215
+ const deepInfraKimiFallbackApiKey =
216
+ shouldUseKimiPlatformFallback && normalizedAgentProvider !== "deepinfra"
217
+ ? getNoloKey("deepinfra")
218
+ : null;
219
+ const openRouterKimiFallbackApiKey = shouldUseKimiPlatformFallback
220
+ ? getNoloKey("openrouter")
221
+ : null;
222
+ const configuredFallbackApiKey = shouldUseConfiguredFallback
223
+ ? getNoloKey("openrouter")
224
+ : null;
225
+ const deepInfraKimiFallbackUrl = getApiEndpoint({
226
+ provider: "deepinfra",
227
+ model: DEEPINFRA_KIMI_FALLBACK_MODEL,
228
+ } as any);
229
+ const fireworksKimiFallbackUrl = getApiEndpoint({
230
+ provider: "fireworks",
231
+ model: FIREWORKS_KIMI_CURRENT_MODEL,
232
+ } as any);
233
+ const buildProviderCompatibleBody = (
234
+ sourceBody: Record<string, any>,
235
+ providerName: string,
236
+ modelName: string,
237
+ ) =>
238
+ normalizeChatCompletionsBodyForProvider({
239
+ body: sourceBody,
240
+ provider: providerName,
241
+ model: modelName,
242
+ });
243
+ const googleFallbackRequest =
244
+ shouldUseGoogleOpenRouterFallback && openRouterApiKey
245
+ ? {
246
+ url: OPENROUTER_CHAT_COMPLETIONS_URL,
247
+ provider: "openrouter",
248
+ model: toOpenRouterGoogleModelName(agentConfig.model),
249
+ init: {
250
+ method: "POST",
251
+ headers: buildUpstreamHeaders(
252
+ OPENROUTER_CHAT_COMPLETIONS_URL,
253
+ openRouterApiKey,
254
+ ),
255
+ body: JSON.stringify(
256
+ buildProviderCompatibleBody(
257
+ body,
258
+ "openrouter",
259
+ toOpenRouterGoogleModelName(agentConfig.model),
260
+ ),
261
+ ),
262
+ signal: init.signal,
263
+ } satisfies RequestInit,
264
+ }
265
+ : undefined;
266
+ const kimiPlatformFallbackRequests = shouldUseKimiPlatformFallback
267
+ ? [
268
+ fireworksKimiFallbackApiKey
269
+ ? {
270
+ url: fireworksKimiFallbackUrl,
271
+ provider: "fireworks",
272
+ model: FIREWORKS_KIMI_CURRENT_MODEL,
273
+ init: {
274
+ method: "POST",
275
+ headers: buildUpstreamHeaders(
276
+ fireworksKimiFallbackUrl,
277
+ fireworksKimiFallbackApiKey,
278
+ ),
279
+ body: JSON.stringify(
280
+ buildProviderCompatibleBody(
281
+ body,
282
+ "fireworks",
283
+ FIREWORKS_KIMI_CURRENT_MODEL,
284
+ ),
285
+ ),
286
+ signal: init.signal,
287
+ } satisfies RequestInit,
288
+ }
289
+ : undefined,
290
+ deepInfraKimiFallbackApiKey
291
+ ? {
292
+ url: deepInfraKimiFallbackUrl,
293
+ provider: "deepinfra",
294
+ model: DEEPINFRA_KIMI_FALLBACK_MODEL,
295
+ init: {
296
+ method: "POST",
297
+ headers: buildUpstreamHeaders(
298
+ deepInfraKimiFallbackUrl,
299
+ deepInfraKimiFallbackApiKey,
300
+ ),
301
+ body: JSON.stringify(
302
+ buildProviderCompatibleBody(
303
+ body,
304
+ "deepinfra",
305
+ DEEPINFRA_KIMI_FALLBACK_MODEL,
306
+ ),
307
+ ),
308
+ signal: init.signal,
309
+ } satisfies RequestInit,
310
+ }
311
+ : undefined,
312
+ openRouterKimiFallbackApiKey
313
+ ? {
314
+ url: OPENROUTER_CHAT_COMPLETIONS_URL,
315
+ provider: "openrouter",
316
+ model: OPENROUTER_KIMI_FALLBACK_MODEL,
317
+ init: {
318
+ method: "POST",
319
+ headers: buildUpstreamHeaders(
320
+ OPENROUTER_CHAT_COMPLETIONS_URL,
321
+ openRouterKimiFallbackApiKey,
322
+ ),
323
+ body: JSON.stringify(
324
+ buildProviderCompatibleBody(
325
+ body,
326
+ "openrouter",
327
+ OPENROUTER_KIMI_FALLBACK_MODEL,
328
+ ),
329
+ ),
330
+ signal: init.signal,
331
+ } satisfies RequestInit,
332
+ }
333
+ : undefined,
334
+ ].filter((request): request is NonNullable<typeof request> => !!request)
335
+ : [];
336
+ const configuredFallbackRequest =
337
+ shouldUseConfiguredFallback && configuredFallbackApiKey
338
+ ? {
339
+ url: OPENROUTER_CHAT_COMPLETIONS_URL,
340
+ provider: "openrouter",
341
+ model: agentConfig.fallbackModel,
342
+ init: {
343
+ method: "POST",
344
+ headers: buildUpstreamHeaders(
345
+ OPENROUTER_CHAT_COMPLETIONS_URL,
346
+ configuredFallbackApiKey,
347
+ ),
348
+ body: JSON.stringify(
349
+ buildProviderCompatibleBody(
350
+ body,
351
+ "openrouter",
352
+ agentConfig.fallbackModel,
353
+ ),
354
+ ),
355
+ signal: init.signal,
356
+ } satisfies RequestInit,
357
+ }
358
+ : undefined;
359
+
360
+ if (
361
+ String(agentConfig?.provider || "").toLowerCase() === "google" &&
362
+ isGoogleNativeImageModel(agentConfig?.model)
363
+ ) {
364
+ try {
365
+ if (body.stream) {
366
+ return {
367
+ response: await fetchGoogleNativeImageChat({
368
+ model: agentConfig.model,
369
+ body,
370
+ apiKey,
371
+ signal: init.signal,
372
+ streamHeaders: {
373
+ "Content-Type": "text/event-stream",
374
+ },
375
+ }),
376
+ provider: agentConfig.provider,
377
+ model: agentConfig.model,
378
+ inputPrice: agentConfig.inputPrice,
379
+ outputPrice: agentConfig.outputPrice,
380
+ };
381
+ }
382
+
383
+ return {
384
+ response: await fetchGoogleNativeImageCompletion({
385
+ model: agentConfig.model,
386
+ body,
387
+ apiKey,
388
+ signal: init.signal,
389
+ }),
390
+ provider: agentConfig.provider,
391
+ model: agentConfig.model,
392
+ inputPrice: agentConfig.inputPrice,
393
+ outputPrice: agentConfig.outputPrice,
394
+ };
395
+ } catch (error: any) {
396
+ const shouldFallbackToOpenRouter =
397
+ !!googleFallbackRequest &&
398
+ (!("status" in (error ?? {})) ||
399
+ GOOGLE_FALLBACK_STATUSES.includes(Number(error?.status)));
400
+ if (!shouldFallbackToOpenRouter) {
401
+ throw error;
402
+ }
403
+ }
404
+ }
405
+
406
+ try {
407
+ const requestArgs = googleFallbackRequest
408
+ ? {
409
+ url: endpoint,
410
+ init,
411
+ provider: agentConfig.provider,
412
+ model: agentConfig.model,
413
+ log: console,
414
+ fallbackRequest: googleFallbackRequest,
415
+ fallbackOnStatuses:
416
+ String(agentConfig?.provider || "").toLowerCase() === "google"
417
+ ? GOOGLE_FALLBACK_STATUSES
418
+ : undefined,
419
+ }
420
+ : kimiPlatformFallbackRequests.length > 0
421
+ ? {
422
+ url: endpoint,
423
+ init,
424
+ provider: agentConfig.provider,
425
+ model: agentConfig.model,
426
+ log: console,
427
+ fallbackRequests: kimiPlatformFallbackRequests,
428
+ fallbackOnStatuses: KIMI_PLATFORM_FALLBACK_STATUSES,
429
+ }
430
+ : configuredFallbackRequest
431
+ ? {
432
+ url: endpoint,
433
+ init,
434
+ provider: agentConfig.provider,
435
+ model: agentConfig.model,
436
+ log: console,
437
+ fallbackRequest: configuredFallbackRequest,
438
+ fallbackOnStatuses: FIREWORKS_OPENROUTER_FALLBACK_STATUSES,
439
+ }
440
+ : {
441
+ url: endpoint,
442
+ init,
443
+ provider: agentConfig.provider,
444
+ model: agentConfig.model,
445
+ log: console,
446
+ };
447
+ const upstreamResponse = await fetchUpstreamWithGentleRetry({
448
+ ...requestArgs,
449
+ });
450
+ const actualProvider = upstreamResponse.provider;
451
+ const actualModel = upstreamResponse.model;
452
+ const actualModelConfig =
453
+ actualProvider && actualModel
454
+ ? findModelConfig(String(actualProvider), String(actualModel))
455
+ : null;
456
+ return {
457
+ response: upstreamResponse.response,
458
+ provider: actualProvider,
459
+ model: actualModel,
460
+ inputPrice: actualModelConfig?.price.input ?? agentConfig.inputPrice,
461
+ outputPrice: actualModelConfig?.price.output ?? agentConfig.outputPrice,
462
+ };
463
+ } catch (error: any) {
464
+ if (error?.name === "TimeoutError" || error?.name === "AbortError") {
465
+ throw new Error(`LLM API timeout after ${llmRequestTimeoutMs}ms: ${endpoint}`);
466
+ }
467
+ throw error;
468
+ }
469
+ }
470
+
471
+ function extractAssistantText(message: ChatMessage | null | undefined): string {
472
+ if (!message) return "";
473
+ return typeof message.content === "string"
474
+ ? message.content
475
+ : (message.content as any[] | null)
476
+ ?.filter((p: any) => p.type === "text")
477
+ .map((p: any) => p.text ?? "")
478
+ .join("") ?? "";
479
+ }
480
+
481
+ const FINAL_PARALLEL_SUMMARY_REPAIR_PROMPT =
482
+ "你刚刚没有给出最终文本回答。请基于上面的并行分支内容,直接输出最终结论,不要再调用任何工具,也不要留空。至少明确给出:共识、保留分歧、下一步。";
483
+
484
+ const EMPTY_ASSISTANT_REPAIR_PROMPT =
485
+ "你刚刚返回了空消息。请继续完成当前任务:如果需要工具就调用工具,否则直接输出可执行的简短结果;不要留空。";
486
+
487
+ function hasParallelBranchMessages(messages: ChatMessage[]): boolean {
488
+ return messages.some(
489
+ (message) =>
490
+ message.role === "assistant" &&
491
+ typeof message.parallelSessionId === "string" &&
492
+ !!message.parallelSessionId.trim(),
493
+ );
494
+ }
495
+
496
+ function extractMessageText(message: ChatMessage | null | undefined): string {
497
+ if (!message) return "";
498
+ return typeof message.content === "string"
499
+ ? message.content
500
+ : (message.content as any[] | null)
501
+ ?.filter((part: any) => part?.type === "text")
502
+ .map((part: any) => part.text ?? "")
503
+ .join("") ?? "";
504
+ }
505
+
506
+ function buildCliTaskPrompt(
507
+ initialMessages: ChatMessage[],
508
+ userInput?: string | null,
509
+ ): string {
510
+ // CLI agent 只接文本上下文,不把本地 tool 轨迹伪装成可重放的远端工具调用。
511
+ const history = initialMessages
512
+ .filter((message) => message.role === "user" || message.role === "assistant")
513
+ .map((message) => {
514
+ const text = extractMessageText(message).trim();
515
+ if (!text) return null;
516
+ const roleLabel =
517
+ message.role === "user"
518
+ ? "用户"
519
+ : "助手";
520
+ return `[${roleLabel}]\n${text}`;
521
+ })
522
+ .filter((message): message is string => !!message)
523
+ .slice(-6);
524
+
525
+ const explicitUserInput = userInput?.trim() ?? "";
526
+ if (explicitUserInput) {
527
+ const explicitUserEntry = `[用户]\n${explicitUserInput}`;
528
+ if (history.at(-1) !== explicitUserEntry) {
529
+ history.push(explicitUserEntry);
530
+ }
531
+ }
532
+
533
+ if (history.length === 0) return explicitUserInput;
534
+ if (history.length === 1 && history[0].startsWith("[用户]\n")) {
535
+ return history[0].slice("[用户]\n".length);
536
+ }
537
+
538
+ return `请基于以下最近对话继续完成当前任务。\n\n${history.join("\n\n")}`;
539
+ }
540
+
541
+ function appendTextPart(
542
+ content: NonNullable<Exclude<ChatMessage["content"], string>>,
543
+ text: string,
544
+ ) {
545
+ if (!text) return;
546
+ const lastPart = content.at(-1);
547
+ if (lastPart?.type === "text") {
548
+ lastPart.text += text;
549
+ return;
550
+ }
551
+ content.push({ type: "text", text });
552
+ }
553
+
554
+ function finalizeAssistantContent(
555
+ content: NonNullable<Exclude<ChatMessage["content"], string>>,
556
+ ): ChatMessage["content"] {
557
+ if (content.length === 0) return null;
558
+ if (content.every((part) => part.type === "text")) {
559
+ return content.map((part) => part.text).join("");
560
+ }
561
+ return content;
562
+ }
563
+
564
+ function parseToolResult(content: string | null | undefined): any {
565
+ if (typeof content !== "string" || !content.trim()) return null;
566
+ try {
567
+ return JSON.parse(content);
568
+ } catch {
569
+ return null;
570
+ }
571
+ }
572
+
573
+ function pickString(value: unknown): string | null {
574
+ return typeof value === "string" && value.trim() ? value.trim() : null;
575
+ }
576
+
577
+ function getToolError(parsed: any): string | null {
578
+ if (!parsed || typeof parsed !== "object") return null;
579
+ const error = pickString(parsed.error);
580
+ if (error) return error;
581
+ if (parsed.ok === false) return "tool returned ok=false";
582
+ if (parsed.success === false) return "tool returned success=false";
583
+ return null;
584
+ }
585
+
586
+ const TASK_RUN_DIRECT_TOOL_NAMES = new Set([
587
+ "taskRun",
588
+ "readTaskContext",
589
+ "queryReadyWorkItems",
590
+ "upsertWorkItem",
591
+ "claimWorkItem",
592
+ "openDialog",
593
+ "recordArtifact",
594
+ "setBlocker",
595
+ "clearBlocker",
596
+ "requestReview",
597
+ "recordReview",
598
+ "submitOutcome",
599
+ "closeWorkItem",
600
+ ]);
601
+
602
+ function normalizeDirectCompletionToolName(toolName: string | undefined): string {
603
+ return canonicalizeToolName((toolName ?? "").replace(/^functions\./, "").trim());
604
+ }
605
+
606
+ function buildStartAgentDialogDirectCompletion(parsed: any): string | null {
607
+ if (!parsed || typeof parsed !== "object") return null;
608
+ const error = getToolError(parsed);
609
+ if (error) {
610
+ const detail = pickString(parsed.detail);
611
+ return [
612
+ "startAgentDialog failed.",
613
+ `error: ${error}`,
614
+ detail ? `detail: ${detail}` : "",
615
+ ].filter(Boolean).join("\n");
616
+ }
617
+
618
+ if (parsed.success !== true) return null;
619
+ const childDialogId = pickString(parsed.childDialogId);
620
+ if (!childDialogId) {
621
+ return [
622
+ "startAgentDialog returned success=true but did not return childDialogId.",
623
+ "Treat this handoff as unverified; do not claim the child agent started or completed.",
624
+ ].join("\n");
625
+ }
626
+
627
+ const lines = [
628
+ "startAgentDialog accepted the handoff. This means the child dialog was started or queued, not completed.",
629
+ `childDialogId: ${childDialogId}`,
630
+ pickString(parsed.status) ? `status: ${pickString(parsed.status)}` : "",
631
+ pickString(parsed.targetAgentKey) ? `targetAgentKey: ${pickString(parsed.targetAgentKey)}` : "",
632
+ pickString(parsed.parentDialogId) ? `parentDialogId: ${pickString(parsed.parentDialogId)}` : "",
633
+ ].filter(Boolean);
634
+
635
+ return lines.join("\n");
636
+ }
637
+
638
+ function buildTaskRunDirectCompletion(parsed: any): string | null {
639
+ if (!parsed || typeof parsed !== "object") return null;
640
+ const error = getToolError(parsed);
641
+ if (error) {
642
+ return [
643
+ "taskRun failed.",
644
+ `error: ${error}`,
645
+ "No task status or outcome was written by this tool call.",
646
+ ].join("\n");
647
+ }
648
+
649
+ return null;
650
+ }
651
+
652
+ function buildUpdateTableRowDirectCompletion(parsed: any): string | null {
653
+ if (!parsed || typeof parsed !== "object") return null;
654
+ const error = getToolError(parsed);
655
+ if (error) {
656
+ return [
657
+ "updateTableRow failed.",
658
+ `error: ${error}`,
659
+ "No table row update was confirmed by this tool call.",
660
+ ].join("\n");
661
+ }
662
+
663
+ return null;
664
+ }
665
+
666
+ function buildDirectCompletionFromToolResult(toolName: string | undefined, content: string): string | null {
667
+ const normalizedToolName = normalizeDirectCompletionToolName(toolName);
668
+ const parsed = parseToolResult(content);
669
+ if (!parsed || typeof parsed !== "object") return null;
670
+
671
+ if (normalizedToolName === "startAgentDialog") {
672
+ return buildStartAgentDialogDirectCompletion(parsed);
673
+ }
674
+ if (TASK_RUN_DIRECT_TOOL_NAMES.has(normalizedToolName)) {
675
+ return buildTaskRunDirectCompletion(parsed);
676
+ }
677
+ if (normalizedToolName === "updateTableRow") {
678
+ return buildUpdateTableRowDirectCompletion(parsed);
679
+ }
680
+
681
+ if (normalizedToolName !== "remotionRenderVideo") return null;
682
+ if (parsed.ok === false || parsed.error) return null;
683
+ if (typeof parsed.fileId !== "string" || !parsed.fileId.trim()) return null;
684
+
685
+ const lines = [
686
+ parsed.text || "Remotion 视频已生成。",
687
+ `fileId: ${parsed.fileId.trim()}`,
688
+ typeof parsed.fileDbKey === "string" && parsed.fileDbKey.trim()
689
+ ? `fileDbKey: ${parsed.fileDbKey.trim()}`
690
+ : "",
691
+ typeof parsed.url === "string" && parsed.url.trim()
692
+ ? `url: ${parsed.url.trim()}`
693
+ : "",
694
+ typeof parsed.template === "string" && parsed.template.trim()
695
+ ? `template: ${parsed.template.trim()}`
696
+ : "",
697
+ ].filter(Boolean);
698
+
699
+ return lines.join("\n");
700
+ }
701
+
702
+ function appendParallelBranchMessages(messages: ChatMessage[], parsed: any) {
703
+ if (!Array.isArray(parsed?.results)) return;
704
+ for (const [index, item] of parsed.results.entries()) {
705
+ if (!item?.ok || typeof item?.content !== "string" || !item.content.trim()) {
706
+ continue;
707
+ }
708
+ messages.push({
709
+ role: "assistant",
710
+ content: item.content,
711
+ cybotKey: item.agentKey,
712
+ agentKey: item.agentKey,
713
+ agentName:
714
+ typeof item.agentName === "string" && item.agentName.trim()
715
+ ? item.agentName.trim()
716
+ : typeof item.label === "string" && item.label.trim()
717
+ ? item.label.trim()
718
+ : undefined,
719
+ parallelSessionId:
720
+ typeof parsed.parallelSessionId === "string"
721
+ ? parsed.parallelSessionId
722
+ : undefined,
723
+ parallelBranchId:
724
+ typeof item.branchId === "string" ? item.branchId : undefined,
725
+ parallelLabel:
726
+ typeof item.label === "string" ? item.label : undefined,
727
+ parallelIndex:
728
+ typeof item.parallelIndex === "number" ? item.parallelIndex : index,
729
+ });
730
+ }
731
+ }
732
+
733
+ function normalizeLoopMessageContent(
734
+ content: ChatMessage["content"] | Record<string, unknown> | undefined,
735
+ ): ChatMessage["content"] {
736
+ if (typeof content === "string" || Array.isArray(content) || content === null) {
737
+ return content;
738
+ }
739
+ if (content == null) return null;
740
+ return JSON.stringify(content);
741
+ }
742
+
743
+ const MAX_EXEC_SHELL_COMMAND_CHARS = 500;
744
+ const MAX_EXEC_SHELL_STDOUT_CHARS = 4000;
745
+ const MAX_EXEC_SHELL_STDERR_CHARS = 2000;
746
+ const MAX_READ_FILE_CONTENT_CHARS = 6000;
747
+ const MAX_READ_DOC_CONTENT_CHARS = 6000;
748
+ const MAX_FETCH_WEBPAGE_CONTENT_CHARS = 6000;
749
+ const MAX_SEARCH_RESULT_ITEMS = 8;
750
+ const MAX_SEARCH_PATH_CHARS = 240;
751
+ const MAX_SEARCH_PREVIEW_CHARS = 220;
752
+ const MAX_IMAGE_TOOL_TEXT_CHARS = 1200;
753
+
754
+ function compactTextForUpstream(value: string, maxChars: number): string {
755
+ if (value.length <= maxChars) return value;
756
+ const hiddenChars = value.length - maxChars;
757
+ return `${value.slice(0, maxChars)}\n...[truncated ${hiddenChars} chars]`;
758
+ }
759
+
760
+ function compactSearchPathForUpstream(value: unknown): string | undefined {
761
+ if (typeof value !== "string" || !value) return undefined;
762
+ return compactTextForUpstream(value, MAX_SEARCH_PATH_CHARS);
763
+ }
764
+
765
+ function compactCodeSearchHitsForUpstream(hits: unknown): Array<Record<string, unknown>> | undefined {
766
+ if (!Array.isArray(hits) || hits.length === 0) return undefined;
767
+ return hits.slice(0, MAX_SEARCH_RESULT_ITEMS).flatMap((entry) => {
768
+ if (!entry || typeof entry !== "object") return [];
769
+ const item: Record<string, unknown> = {};
770
+ const path = compactSearchPathForUpstream((entry as any).path);
771
+ if (path) item.path = path;
772
+ if (typeof (entry as any).line === "number") item.line = (entry as any).line;
773
+ if (typeof (entry as any).preview === "string" && (entry as any).preview) {
774
+ item.preview = compactTextForUpstream(
775
+ (entry as any).preview,
776
+ MAX_SEARCH_PREVIEW_CHARS,
777
+ );
778
+ }
779
+ return Object.keys(item).length > 0 ? [item] : [];
780
+ });
781
+ }
782
+
783
+ function compactCodeSearchFilesForUpstream(files: unknown): Array<Record<string, unknown>> | undefined {
784
+ if (!Array.isArray(files) || files.length === 0) return undefined;
785
+ return files.slice(0, MAX_SEARCH_RESULT_ITEMS).flatMap((entry) => {
786
+ if (!entry || typeof entry !== "object") return [];
787
+ const path = compactSearchPathForUpstream((entry as any).path);
788
+ return path ? [{ path }] : [];
789
+ });
790
+ }
791
+
792
+ function compactToolContentForUpstream(
793
+ toolName: string | undefined,
794
+ content: ChatMessage["content"],
795
+ ): ChatMessage["content"] {
796
+ if (typeof content !== "string") {
797
+ return normalizeLoopMessageContent(content);
798
+ }
799
+ const normalizedToolName = canonicalizeToolName(toolName ?? "");
800
+ const parsed = parseToolResult(content);
801
+ if (!parsed || typeof parsed !== "object") {
802
+ return content;
803
+ }
804
+
805
+ if (normalizedToolName === "execShell") {
806
+ return JSON.stringify({
807
+ ...(typeof parsed.ok === "boolean" ? { ok: parsed.ok } : {}),
808
+ ...(typeof parsed.command === "string"
809
+ ? {
810
+ command: compactTextForUpstream(
811
+ parsed.command,
812
+ MAX_EXEC_SHELL_COMMAND_CHARS,
813
+ ),
814
+ }
815
+ : {}),
816
+ ...(typeof parsed.exitCode === "number" ? { exitCode: parsed.exitCode } : {}),
817
+ ...(typeof parsed.stdout === "string" && parsed.stdout
818
+ ? {
819
+ stdout: compactTextForUpstream(
820
+ parsed.stdout,
821
+ MAX_EXEC_SHELL_STDOUT_CHARS,
822
+ ),
823
+ }
824
+ : {}),
825
+ ...(typeof parsed.stderr === "string" && parsed.stderr
826
+ ? {
827
+ stderr: compactTextForUpstream(
828
+ parsed.stderr,
829
+ MAX_EXEC_SHELL_STDERR_CHARS,
830
+ ),
831
+ }
832
+ : {}),
833
+ ...(typeof parsed.error === "string" && parsed.error
834
+ ? { error: compactTextForUpstream(parsed.error, MAX_EXEC_SHELL_STDERR_CHARS) }
835
+ : {}),
836
+ });
837
+ }
838
+
839
+ if (normalizedToolName === "readFile") {
840
+ return JSON.stringify({
841
+ ...(typeof parsed.ok === "boolean" ? { ok: parsed.ok } : {}),
842
+ ...(typeof parsed.filePath === "string" ? { filePath: parsed.filePath } : {}),
843
+ ...(typeof parsed.lineCount === "number" ? { lineCount: parsed.lineCount } : {}),
844
+ ...(typeof parsed.totalLineCount === "number"
845
+ ? { totalLineCount: parsed.totalLineCount }
846
+ : {}),
847
+ ...(typeof parsed.lineOffset === "number" ? { lineOffset: parsed.lineOffset } : {}),
848
+ ...(typeof parsed.rangeStart === "number" ? { rangeStart: parsed.rangeStart } : {}),
849
+ ...(typeof parsed.rangeEnd === "number" ? { rangeEnd: parsed.rangeEnd } : {}),
850
+ ...(typeof parsed.adjustedEndLine === "number"
851
+ ? { adjustedEndLine: parsed.adjustedEndLine }
852
+ : {}),
853
+ ...(parsed.nextSymbol && typeof parsed.nextSymbol === "object"
854
+ ? { nextSymbol: parsed.nextSymbol }
855
+ : {}),
856
+ ...(typeof parsed.content === "string"
857
+ ? {
858
+ content: compactTextForUpstream(
859
+ parsed.content,
860
+ MAX_READ_FILE_CONTENT_CHARS,
861
+ ),
862
+ }
863
+ : {}),
864
+ ...(typeof parsed.error === "string" && parsed.error
865
+ ? { error: compactTextForUpstream(parsed.error, MAX_EXEC_SHELL_STDERR_CHARS) }
866
+ : {}),
867
+ });
868
+ }
869
+
870
+ if (normalizedToolName === "readDoc" || normalizedToolName === "readPage") {
871
+ return JSON.stringify({
872
+ ...(typeof parsed.success === "boolean" ? { success: parsed.success } : {}),
873
+ ...(typeof parsed.id === "string" ? { id: parsed.id } : {}),
874
+ ...(typeof parsed.title === "string" ? { title: parsed.title } : {}),
875
+ ...(typeof parsed.content === "string"
876
+ ? {
877
+ content: compactTextForUpstream(
878
+ parsed.content,
879
+ MAX_READ_DOC_CONTENT_CHARS,
880
+ ),
881
+ }
882
+ : {}),
883
+ ...(parsed.metadata && typeof parsed.metadata === "object"
884
+ ? { metadata: parsed.metadata }
885
+ : {}),
886
+ ...(typeof parsed.error === "string" && parsed.error
887
+ ? { error: compactTextForUpstream(parsed.error, MAX_EXEC_SHELL_STDERR_CHARS) }
888
+ : {}),
889
+ });
890
+ }
891
+
892
+ if (normalizedToolName === "fetchWebpage") {
893
+ return JSON.stringify({
894
+ ...(typeof parsed.preview === "string"
895
+ ? {
896
+ preview: compactTextForUpstream(
897
+ parsed.preview,
898
+ MAX_SEARCH_PREVIEW_CHARS,
899
+ ),
900
+ }
901
+ : {}),
902
+ ...(typeof parsed.fullContent === "string"
903
+ ? {
904
+ fullContent: compactTextForUpstream(
905
+ parsed.fullContent,
906
+ MAX_FETCH_WEBPAGE_CONTENT_CHARS,
907
+ ),
908
+ }
909
+ : {}),
910
+ ...(typeof parsed.rawData === "string"
911
+ ? {
912
+ rawData: compactTextForUpstream(
913
+ parsed.rawData,
914
+ MAX_FETCH_WEBPAGE_CONTENT_CHARS,
915
+ ),
916
+ }
917
+ : {}),
918
+ ...(typeof parsed.displayData === "string"
919
+ ? {
920
+ displayData: compactTextForUpstream(
921
+ parsed.displayData,
922
+ MAX_FETCH_WEBPAGE_CONTENT_CHARS,
923
+ ),
924
+ }
925
+ : {}),
926
+ ...(typeof parsed.contentType === "string" ? { contentType: parsed.contentType } : {}),
927
+ ...(typeof parsed.markdownTokens === "string" || typeof parsed.markdownTokens === "number"
928
+ ? { markdownTokens: parsed.markdownTokens }
929
+ : {}),
930
+ ...(typeof parsed.error === "string" && parsed.error
931
+ ? { error: compactTextForUpstream(parsed.error, MAX_EXEC_SHELL_STDERR_CHARS) }
932
+ : {}),
933
+ });
934
+ }
935
+
936
+ if (normalizedToolName === "openAIGptImage") {
937
+ const files = Array.isArray(parsed.files)
938
+ ? parsed.files.flatMap((entry) => {
939
+ if (!entry || typeof entry !== "object") return [];
940
+ return [{
941
+ ...(
942
+ typeof (entry as any).fileId === "string"
943
+ ? { fileId: (entry as any).fileId }
944
+ : {}
945
+ ),
946
+ ...(
947
+ (entry as any).metadata && typeof (entry as any).metadata === "object"
948
+ ? { metadata: (entry as any).metadata }
949
+ : {}
950
+ ),
951
+ }];
952
+ })
953
+ : undefined;
954
+
955
+ return JSON.stringify({
956
+ ...(typeof parsed.text === "string"
957
+ ? { text: compactTextForUpstream(parsed.text, MAX_IMAGE_TOOL_TEXT_CHARS) }
958
+ : {}),
959
+ ...(typeof parsed.imageCount === "number" ? { imageCount: parsed.imageCount } : {}),
960
+ ...(Array.isArray(parsed.revisedPrompts)
961
+ ? {
962
+ revisedPrompts: parsed.revisedPrompts
963
+ .filter((item: unknown): item is string => typeof item === "string" && !!item)
964
+ .map((item: string) => compactTextForUpstream(item, MAX_IMAGE_TOOL_TEXT_CHARS)),
965
+ }
966
+ : {}),
967
+ ...(files ? { files } : {}),
968
+ ...(typeof parsed.error === "string" && parsed.error
969
+ ? { error: compactTextForUpstream(parsed.error, MAX_EXEC_SHELL_STDERR_CHARS) }
970
+ : {}),
971
+ });
972
+ }
973
+
974
+ if (normalizedToolName === "codeSearch") {
975
+ return JSON.stringify({
976
+ ...(typeof parsed.ok === "boolean" ? { ok: parsed.ok } : {}),
977
+ ...(typeof parsed.mode === "string" ? { mode: parsed.mode } : {}),
978
+ ...(typeof parsed.query === "string" ? { query: parsed.query } : {}),
979
+ ...(typeof parsed.path === "string" ? { path: parsed.path } : {}),
980
+ ...(typeof parsed.glob === "string" || parsed.glob === null
981
+ ? { glob: parsed.glob }
982
+ : {}),
983
+ ...(typeof parsed.truncated === "boolean" ? { truncated: parsed.truncated } : {}),
984
+ ...(typeof parsed.totalHits === "number" ? { totalHits: parsed.totalHits } : {}),
985
+ ...(typeof parsed.totalFiles === "number" ? { totalFiles: parsed.totalFiles } : {}),
986
+ ...(compactCodeSearchHitsForUpstream(parsed.hits)
987
+ ? { hits: compactCodeSearchHitsForUpstream(parsed.hits) }
988
+ : {}),
989
+ ...(compactCodeSearchFilesForUpstream(parsed.files)
990
+ ? { files: compactCodeSearchFilesForUpstream(parsed.files) }
991
+ : {}),
992
+ ...(typeof parsed.error === "string" && parsed.error
993
+ ? { error: compactTextForUpstream(parsed.error, MAX_EXEC_SHELL_STDERR_CHARS) }
994
+ : {}),
995
+ });
996
+ }
997
+
998
+ return content;
999
+ }
1000
+
1001
+ function shouldCompactImmediateToolReplay(toolName: string | undefined): boolean {
1002
+ const normalizedToolName = canonicalizeToolName(toolName ?? "");
1003
+ return (
1004
+ normalizedToolName === "execShell" ||
1005
+ normalizedToolName === "openAIGptImage"
1006
+ );
1007
+ }
1008
+
1009
+ function sanitizeLoopMessageForUpstream(message: ChatMessage): ChatMessage {
1010
+ const sanitized: ChatMessage = {
1011
+ role: message.role,
1012
+ content: normalizeLoopMessageContent(message.content),
1013
+ };
1014
+
1015
+ if (
1016
+ message.role === "assistant" &&
1017
+ typeof message.reasoning_content === "string" &&
1018
+ message.reasoning_content
1019
+ ) {
1020
+ sanitized.reasoning_content = message.reasoning_content;
1021
+ }
1022
+
1023
+ if (
1024
+ message.role === "assistant" &&
1025
+ Array.isArray(message.tool_calls) &&
1026
+ message.tool_calls.length > 0
1027
+ ) {
1028
+ sanitized.tool_calls = message.tool_calls;
1029
+ }
1030
+
1031
+ if (
1032
+ message.role === "tool" &&
1033
+ typeof message.tool_call_id === "string" &&
1034
+ message.tool_call_id.trim()
1035
+ ) {
1036
+ sanitized.tool_call_id = message.tool_call_id.trim();
1037
+ }
1038
+
1039
+ return sanitized;
1040
+ }
1041
+
1042
+ function sanitizeLoopMessagesForUpstream(messages: ChatMessage[]): ChatMessage[] {
1043
+ const toolNameByCallId = new Map<string, string>();
1044
+ let firstTrailingToolIndex = messages.length;
1045
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1046
+ if (messages[index]?.role !== "tool") {
1047
+ break;
1048
+ }
1049
+ firstTrailingToolIndex = index;
1050
+ }
1051
+ return messages.map((message, index) => {
1052
+ const sanitized = sanitizeLoopMessageForUpstream(message);
1053
+
1054
+ if (
1055
+ message.role === "assistant" &&
1056
+ Array.isArray(message.tool_calls) &&
1057
+ message.tool_calls.length > 0
1058
+ ) {
1059
+ for (const toolCall of message.tool_calls) {
1060
+ if (
1061
+ typeof toolCall?.id === "string" &&
1062
+ toolCall.id.trim() &&
1063
+ typeof toolCall?.function?.name === "string"
1064
+ ) {
1065
+ toolNameByCallId.set(toolCall.id.trim(), toolCall.function.name);
1066
+ }
1067
+ }
1068
+ sanitized.content = null;
1069
+ return sanitized;
1070
+ }
1071
+
1072
+ if (
1073
+ message.role === "tool" &&
1074
+ typeof sanitized.tool_call_id === "string" &&
1075
+ sanitized.tool_call_id
1076
+ ) {
1077
+ const toolName = toolNameByCallId.get(sanitized.tool_call_id);
1078
+ const shouldCompactImmediately = shouldCompactImmediateToolReplay(toolName);
1079
+ const isTrailingToolMessage = index >= firstTrailingToolIndex;
1080
+ sanitized.content =
1081
+ isTrailingToolMessage && !shouldCompactImmediately
1082
+ ? normalizeLoopMessageContent(message.content)
1083
+ : compactToolContentForUpstream(toolName, sanitized.content);
1084
+ }
1085
+
1086
+ return sanitized;
1087
+ });
1088
+ }
1089
+
1090
+ function collectLatestImageUrls(messages: ChatMessage[]): string[] {
1091
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1092
+ const content = messages[index]?.content;
1093
+ if (!Array.isArray(content)) continue;
1094
+
1095
+ const urls = content
1096
+ .map((part) =>
1097
+ part?.type === "image_url" && typeof part.image_url?.url === "string"
1098
+ ? part.image_url.url.trim()
1099
+ : ""
1100
+ )
1101
+ .filter((url): url is string => !!url);
1102
+
1103
+ if (urls.length > 0) {
1104
+ return [...new Set(urls)];
1105
+ }
1106
+ }
1107
+
1108
+ return [];
1109
+ }
1110
+
1111
+ function getApiKey(agentConfig: any): string | undefined {
1112
+ // apiSource="custom" 的 agent 使用自己的 apiKey,不走平台 key。
1113
+ // 使用自己的 apiKey,不走平台 key,不要修改这个逻辑
1114
+ const key = agentConfig.apiKey?.trim() || getNoloKey(agentConfig.provider);
1115
+ // 自定义 provider URL 场景(如本地 Ollama)不强制要求 key
1116
+ const hasCustomUrl = !!(agentConfig.customProviderUrl?.trim());
1117
+ if (!key && !hasCustomUrl) {
1118
+ throw new Error(`No API key configured for provider "${agentConfig.provider}".`);
1119
+ }
1120
+ return key || undefined;
1121
+ }
1122
+
1123
+ function buildTools(agentConfig: any): any[] | undefined {
1124
+ if (!agentConfig.tools?.length) return undefined;
1125
+ const tools = prepareTools(agentConfig.tools, {
1126
+ provider: agentConfig.provider,
1127
+ disabledToolNames: process.env.EXA_API_KEY ? [] : ["exa_search"],
1128
+ });
1129
+ return tools.length > 0 ? tools : undefined;
1130
+ }
1131
+
1132
+ function getPreparedToolNames(tools: any[] | undefined): string[] {
1133
+ if (!tools?.length) return [];
1134
+ return tools
1135
+ .map((tool) => tool?.function?.name ?? tool?.name)
1136
+ .filter((name): name is string => typeof name === "string" && !!name.trim());
1137
+ }
1138
+
1139
+ function extractMessageContentText(content: ChatMessage["content"] | undefined | null): string {
1140
+ if (typeof content === "string") return content;
1141
+ if (!Array.isArray(content)) return "";
1142
+ return content
1143
+ .filter((part: any) => part?.type === "text" && typeof part.text === "string")
1144
+ .map((part: any) => part.text)
1145
+ .join("\n");
1146
+ }
1147
+
1148
+ function extractUserInputText(
1149
+ userInput: string | null | undefined,
1150
+ messages: ChatMessage[],
1151
+ ): string {
1152
+ if (typeof userInput === "string" && userInput.trim()) return userInput;
1153
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
1154
+ const message = messages[index];
1155
+ if (message?.role !== "user") continue;
1156
+ const text = extractMessageContentText(message.content);
1157
+ if (text.trim()) return text;
1158
+ }
1159
+ return "";
1160
+ }
1161
+
1162
+ function shouldRunWereadGatewayPreflight(
1163
+ agentConfig: any,
1164
+ tools: any[] | undefined,
1165
+ userInput: string | null | undefined,
1166
+ messages: ChatMessage[],
1167
+ ): boolean {
1168
+ const hasWereadTool =
1169
+ Array.isArray(agentConfig?.tools) &&
1170
+ agentConfig.tools.some((toolName: unknown) => canonicalizeToolName(String(toolName)) === "wereadGateway") &&
1171
+ getPreparedToolNames(tools).some((toolName) => canonicalizeToolName(toolName) === "wereadGateway");
1172
+ if (!hasWereadTool) return false;
1173
+
1174
+ const text = extractUserInputText(userInput, messages).trim();
1175
+ if (!text) return false;
1176
+ return /微信读书|weread|WEREAD|书架|阅读|读书|笔记|划线|想法|书评|报告|统计|进度|搜索|推荐|key|Key|KEY|密钥|授权|连接|连通|链接|配置|保存|添加|测试|检查|验证/.test(text);
1177
+ }
1178
+
1179
+ async function runWereadGatewayPreflightIfNeeded(args: {
1180
+ agentConfig: any;
1181
+ tools: any[] | undefined;
1182
+ messages: ChatMessage[];
1183
+ serverBase: string;
1184
+ authToken: string;
1185
+ runtimePolicy?: ResolvedRuntimePolicy;
1186
+ runtimeState: ToolPolicyRuntimeState;
1187
+ userId?: string | null;
1188
+ dialogId?: string | null;
1189
+ currentSpaceId?: string | null;
1190
+ userInput?: string | null;
1191
+ runtimeContext?: AgentRunRuntimeContext | null;
1192
+ send?: Send;
1193
+ }): Promise<{ ran: boolean; elapsedMs: number; completedAt?: number }> {
1194
+ if (
1195
+ !shouldRunWereadGatewayPreflight(
1196
+ args.agentConfig,
1197
+ args.tools,
1198
+ args.userInput,
1199
+ args.messages,
1200
+ )
1201
+ ) {
1202
+ return { ran: false, elapsedMs: 0 };
1203
+ }
1204
+
1205
+ const toolCall: AssistantToolCall = {
1206
+ id: `call_weread_preflight_${Date.now()}`,
1207
+ type: "function",
1208
+ function: {
1209
+ name: "wereadGateway",
1210
+ arguments: JSON.stringify({ api_name: "/shelf/sync", params: {} }),
1211
+ },
1212
+ };
1213
+ args.messages.push({
1214
+ role: "assistant",
1215
+ content: null,
1216
+ tool_calls: [toolCall],
1217
+ });
1218
+ args.send?.({
1219
+ type: "assistant_tool_calls",
1220
+ content: null,
1221
+ tool_calls: [toolCall],
1222
+ });
1223
+ args.send?.({ type: "tool_start", calls: ["wereadGateway"] });
1224
+
1225
+ const startedAt = Date.now();
1226
+ const toolResult = await executeToolOnServer(toolCall, args.serverBase, args.authToken, {
1227
+ policy: args.runtimePolicy,
1228
+ runtimeState: args.runtimeState,
1229
+ userId: args.userId,
1230
+ agentKey:
1231
+ typeof args.agentConfig?.dbKey === "string" && args.agentConfig.dbKey.trim()
1232
+ ? args.agentConfig.dbKey.trim()
1233
+ : typeof args.agentConfig?.id === "string" && args.agentConfig.id.trim()
1234
+ ? args.agentConfig.id.trim()
1235
+ : null,
1236
+ dialogId: args.dialogId,
1237
+ currentSpaceId: args.currentSpaceId,
1238
+ userInput: args.userInput,
1239
+ runtimeContext: args.runtimeContext,
1240
+ imageUrls: collectLatestImageUrls(args.messages),
1241
+ });
1242
+ const completedAt = Date.now();
1243
+ args.messages.push({
1244
+ role: "tool",
1245
+ tool_call_id: toolCall.id,
1246
+ content: toolResult,
1247
+ });
1248
+ args.messages.push({
1249
+ role: "system",
1250
+ content:
1251
+ "上面的 wereadGateway 是本轮服务端预检得到的真实结果。请基于该结果回答用户;不要声称 wereadGateway 未挂载、不可用或无法调用。若结果包含 MISSING_WEREAD_API_KEY 或缺少 WEREAD_API_KEY,请提示用户去密钥管理保存 WEREAD_API_KEY。",
1252
+ });
1253
+ args.send?.({
1254
+ type: "tool_result",
1255
+ toolCallId: toolCall.id,
1256
+ toolName: "wereadGateway",
1257
+ content: toolResult,
1258
+ });
1259
+ args.send?.({ type: "tool_end" });
1260
+
1261
+ return {
1262
+ ran: true,
1263
+ elapsedMs: Math.max(0, completedAt - startedAt),
1264
+ completedAt,
1265
+ };
1266
+ }
1267
+
1268
+ function buildLoopRequestBody(
1269
+ agentConfig: any,
1270
+ messages: ChatMessage[],
1271
+ tools: any[] | undefined,
1272
+ stream: boolean,
1273
+ ): Record<string, any> {
1274
+ const upstreamMessages = sanitizeLoopMessagesForUpstream(messages);
1275
+ // Keep agent-run aligned with the foreground chat path so image-output agents
1276
+ // inherit the same modalities/image_config contract instead of silently
1277
+ // degrading to plain text.
1278
+ const body: Record<string, any> = generateRequestBody({
1279
+ agentConfig,
1280
+ messages: upstreamMessages as any,
1281
+ prependSystemPrompt: !agentConfig?.runtimePromptAlreadyInMessages,
1282
+ });
1283
+ applyLoopStreamMode(body, stream);
1284
+ if (isResponseAPIModel(agentConfig)) {
1285
+ const responseTools = [
1286
+ ...(toResponsesTools(tools) ?? []),
1287
+ ...(
1288
+ String(agentConfig?.provider || "").toLowerCase() === "openai" &&
1289
+ !getModelInfo(String(agentConfig?.model || ""))?.hasImageOutput &&
1290
+ !!agentConfig?.imageConfig?.enabled
1291
+ ? [{ type: "image_generation" as const }]
1292
+ : []
1293
+ ),
1294
+ ];
1295
+ if (responseTools.length) {
1296
+ body.tools = responseTools;
1297
+ body.tool_choice = "auto";
1298
+ }
1299
+ return body;
1300
+ }
1301
+ if (tools) {
1302
+ body.tools = tools;
1303
+ body.tool_choice = "auto";
1304
+ }
1305
+ if (agentConfig.enableThinking) {
1306
+ const provider = (agentConfig.provider || "").toLowerCase();
1307
+ if (provider === "anthropic") {
1308
+ body.thinking = { type: "enabled", budget_tokens: agentConfig.thinkingBudget ?? 8000 };
1309
+ }
1310
+ } else if (
1311
+ /xiaomimimo\.com/i.test(agentConfig.customProviderUrl ?? "") ||
1312
+ String(agentConfig?.provider || "").toLowerCase() === "mimo"
1313
+ ) {
1314
+ body.thinking = { type: "disabled" };
1315
+ }
1316
+ return body;
1317
+ }
1318
+
1319
+ export function applyLoopStreamMode(body: Record<string, any>, stream: boolean) {
1320
+ body.stream = stream;
1321
+ if (!stream) {
1322
+ delete body.stream_options;
1323
+ }
1324
+ return body;
1325
+ }
1326
+
1327
+ function buildAssistantMessageFromResponse(data: any): ChatMessage | null {
1328
+ const toolCalls = extractToolCallsFromResponseOutput(data);
1329
+ const contentText = extractTextFromResponseOutput(data);
1330
+ const contentImages = extractImagePartsFromResponseOutput(data);
1331
+ const content =
1332
+ contentImages.length > 0
1333
+ ? [
1334
+ ...(contentText ? [{ type: "text", text: contentText }] : []),
1335
+ ...contentImages,
1336
+ ]
1337
+ : contentText;
1338
+ if (!content && toolCalls.length === 0) return null;
1339
+
1340
+ return {
1341
+ role: "assistant",
1342
+ content: content || null,
1343
+ ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
1344
+ };
1345
+ }
1346
+
1347
+ function extractTextFromResponseOutputItem(item: any): string {
1348
+ if (item?.type !== "message" || !Array.isArray(item.content)) return "";
1349
+ return item.content
1350
+ .filter((content: any) => content?.type === "output_text" && typeof content.text === "string")
1351
+ .map((content: any) => content.text)
1352
+ .join("");
1353
+ }
1354
+
1355
+ function buildLatencyProfile(args: {
1356
+ startedAt: number;
1357
+ llmRequestCount: number;
1358
+ llmWaitMs: number;
1359
+ llmJsonParseMs: number;
1360
+ toolExecutionMs: number;
1361
+ firstAssistantAt?: number;
1362
+ firstToolResultAt?: number;
1363
+ }) {
1364
+ const endedAt = Date.now();
1365
+ return {
1366
+ totalMs: Math.max(0, endedAt - args.startedAt),
1367
+ llmRequestCount: args.llmRequestCount,
1368
+ llmWaitMs: args.llmWaitMs,
1369
+ llmJsonParseMs: args.llmJsonParseMs,
1370
+ toolExecutionMs: args.toolExecutionMs,
1371
+ ...(typeof args.firstAssistantAt === "number"
1372
+ ? { timeToFirstAssistantMs: Math.max(0, args.firstAssistantAt - args.startedAt) }
1373
+ : {}),
1374
+ ...(typeof args.firstToolResultAt === "number"
1375
+ ? { timeToFirstToolResultMs: Math.max(0, args.firstToolResultAt - args.startedAt) }
1376
+ : {}),
1377
+ endedAt,
1378
+ };
1379
+ }
1380
+
1381
+ // ── 非流式 ────────────────────────────────────────────────────────────────────
1382
+
1383
+ export async function runAgentLoop(
1384
+ agentConfig: any,
1385
+ initialMessages: ChatMessage[],
1386
+ _maxRounds: number,
1387
+ serverBase: string,
1388
+ authToken: string,
1389
+ runtimePolicy?: ResolvedRuntimePolicy,
1390
+ userId?: string | null,
1391
+ dialogId?: string | null,
1392
+ currentSpaceId?: string | null,
1393
+ userInput?: string | null,
1394
+ runtimeContext?: AgentRunRuntimeContext | null,
1395
+ ): Promise<LoopResult> {
1396
+ const startTime = Date.now();
1397
+ agentConfig = await hydrateApiKeyReferenceIfNeeded(agentConfig, serverBase, authToken);
1398
+ if (agentConfig.apiSource === "cli") {
1399
+ // CLI 与普通 model 共享 prompt/model/最近历史,但这里有意保持“单次文本委托”的边界。
1400
+ const provider: CliProvider = (agentConfig.cliProvider ?? "copilot") as CliProvider;
1401
+ const taskPrompt = buildCliTaskPrompt(initialMessages, userInput);
1402
+ const fullPrompt = buildCliPrompt(agentConfig.prompt, taskPrompt);
1403
+ const result = await executeCli(provider, fullPrompt, {
1404
+ model: agentConfig.model || undefined,
1405
+ yolo: true,
1406
+ });
1407
+
1408
+ return {
1409
+ content: result.text,
1410
+ model: agentConfig.model ?? "unknown",
1411
+ provider: agentConfig.provider ?? undefined,
1412
+ inputPrice: agentConfig.inputPrice,
1413
+ outputPrice: agentConfig.outputPrice,
1414
+ usage: undefined,
1415
+ trace: [
1416
+ {
1417
+ role: "assistant",
1418
+ content: result.text,
1419
+ },
1420
+ ],
1421
+ runtimeToolNames: [],
1422
+ toolCallCount: 0,
1423
+ policyState: {
1424
+ autoKnowledgeCaptureCount:
1425
+ (runtimePolicy?.knowledgeCapture.currentAutoCreates ?? 0),
1426
+ autoSpaceReadCount:
1427
+ (runtimePolicy?.spaceContext.currentAutoReads ?? 0),
1428
+ updatedAt: Date.now(),
1429
+ },
1430
+ latencyProfile: buildLatencyProfile({
1431
+ startedAt: startTime,
1432
+ llmRequestCount: 0,
1433
+ llmWaitMs: 0,
1434
+ llmJsonParseMs: 0,
1435
+ toolExecutionMs: 0,
1436
+ firstAssistantAt: startTime + Math.max(0, result.elapsed ?? 0),
1437
+ }),
1438
+ };
1439
+ }
1440
+
1441
+ const endpoint = getApiEndpoint(agentConfig);
1442
+ const tools = buildTools(agentConfig);
1443
+ const runtimeToolNames = getPreparedToolNames(tools);
1444
+ // maxRounds 仅为兼容旧请求字段保留;工具轮次不再做硬限制。
1445
+ // 后续如需控制,应改为基于成本/预算的策略,而不是轮次数量。
1446
+ void _maxRounds;
1447
+
1448
+ const messages: ChatMessage[] = [...initialMessages];
1449
+ const traceStartIndex = messages.length;
1450
+ let finalContent = "";
1451
+ let lastUsage: Record<string, any> | undefined;
1452
+ let lastResolvedProvider = agentConfig.provider ?? undefined;
1453
+ let lastResolvedModel = agentConfig.model ?? "unknown";
1454
+ let lastResolvedInputPrice = agentConfig.inputPrice;
1455
+ let lastResolvedOutputPrice = agentConfig.outputPrice;
1456
+ let toolCallCount = 0;
1457
+ let llmRequestCount = 0;
1458
+ let llmWaitMs = 0;
1459
+ let llmJsonParseMs = 0;
1460
+ let toolExecutionMs = 0;
1461
+ let firstAssistantAt: number | undefined;
1462
+ let firstToolResultAt: number | undefined;
1463
+ const runtimeState: ToolPolicyRuntimeState = {
1464
+ autoKnowledgeCaptureCountThisRun: 0,
1465
+ autoSpaceReadCountThisRun: 0,
1466
+ };
1467
+ let finalParallelSummaryRepairPending = false;
1468
+ let finalParallelSummaryRepairUsed = false;
1469
+ let emptyAssistantRepairPending = false;
1470
+ let emptyAssistantRepairUsed = false;
1471
+
1472
+ const wereadPreflight = await runWereadGatewayPreflightIfNeeded({
1473
+ agentConfig,
1474
+ tools,
1475
+ messages,
1476
+ serverBase,
1477
+ authToken,
1478
+ runtimePolicy,
1479
+ runtimeState,
1480
+ userId,
1481
+ dialogId,
1482
+ currentSpaceId,
1483
+ userInput,
1484
+ runtimeContext,
1485
+ });
1486
+ if (wereadPreflight.ran) {
1487
+ toolCallCount += 1;
1488
+ toolExecutionMs += wereadPreflight.elapsedMs;
1489
+ firstToolResultAt = wereadPreflight.completedAt;
1490
+ }
1491
+
1492
+ for (;;) {
1493
+ if (Date.now() - startTime > MAX_TIME_MS) break;
1494
+
1495
+ const requestMessages = finalParallelSummaryRepairPending
1496
+ ? [
1497
+ ...messages,
1498
+ { role: "system", content: FINAL_PARALLEL_SUMMARY_REPAIR_PROMPT } satisfies ChatMessage,
1499
+ ]
1500
+ : emptyAssistantRepairPending
1501
+ ? [
1502
+ ...messages,
1503
+ { role: "system", content: EMPTY_ASSISTANT_REPAIR_PROMPT } satisfies ChatMessage,
1504
+ ]
1505
+ : messages;
1506
+ const body = buildLoopRequestBody(agentConfig, requestMessages, tools, false);
1507
+ finalParallelSummaryRepairPending = false;
1508
+ emptyAssistantRepairPending = false;
1509
+
1510
+ const upstreamStartedAt = Date.now();
1511
+ const upstreamResult = await fetchLoopUpstream(endpoint, agentConfig, body);
1512
+ const res = upstreamResult.response;
1513
+ llmRequestCount += 1;
1514
+ llmWaitMs += Math.max(0, Date.now() - upstreamStartedAt);
1515
+ lastResolvedProvider = upstreamResult.provider ?? lastResolvedProvider;
1516
+ lastResolvedModel = upstreamResult.model ?? lastResolvedModel;
1517
+ lastResolvedInputPrice = upstreamResult.inputPrice ?? lastResolvedInputPrice;
1518
+ lastResolvedOutputPrice = upstreamResult.outputPrice ?? lastResolvedOutputPrice;
1519
+
1520
+ if (!res.ok) throw new Error(`LLM API error (${res.status}): ${await res.text()}`);
1521
+
1522
+ const parseStartedAt = Date.now();
1523
+ const data = await res.json() as any;
1524
+ llmJsonParseMs += Math.max(0, Date.now() - parseStartedAt);
1525
+ const choice = data.choices?.[0];
1526
+ const assistantMsg: ChatMessage | null = isResponseAPIModel(agentConfig)
1527
+ ? buildAssistantMessageFromResponse(data)
1528
+ : choice?.message;
1529
+ if (!assistantMsg) break;
1530
+ if (firstAssistantAt === undefined) {
1531
+ firstAssistantAt = Date.now();
1532
+ }
1533
+
1534
+ lastUsage = data.usage ?? data.response?.usage ?? lastUsage;
1535
+ const assistantText = extractAssistantText(assistantMsg).trim();
1536
+ if (
1537
+ !assistantMsg.tool_calls?.length &&
1538
+ !assistantText &&
1539
+ !finalParallelSummaryRepairUsed &&
1540
+ hasParallelBranchMessages(messages)
1541
+ ) {
1542
+ finalParallelSummaryRepairPending = true;
1543
+ finalParallelSummaryRepairUsed = true;
1544
+ continue;
1545
+ }
1546
+ if (
1547
+ !assistantMsg.tool_calls?.length &&
1548
+ !assistantText &&
1549
+ !emptyAssistantRepairUsed
1550
+ ) {
1551
+ emptyAssistantRepairPending = true;
1552
+ emptyAssistantRepairUsed = true;
1553
+ continue;
1554
+ }
1555
+ messages.push(assistantMsg);
1556
+
1557
+ if (!assistantMsg.tool_calls?.length || (!isResponseAPIModel(agentConfig) && choice.finish_reason === "stop")) {
1558
+ finalContent = assistantText;
1559
+ break;
1560
+ }
1561
+
1562
+ for (const tc of assistantMsg.tool_calls!) {
1563
+ toolCallCount++;
1564
+ const toolStartedAt = Date.now();
1565
+ const toolResult = await executeToolOnServer(tc, serverBase, authToken, {
1566
+ policy: runtimePolicy,
1567
+ runtimeState,
1568
+ userId,
1569
+ agentKey:
1570
+ typeof agentConfig?.dbKey === "string" && agentConfig.dbKey.trim()
1571
+ ? agentConfig.dbKey.trim()
1572
+ : typeof agentConfig?.id === "string" && agentConfig.id.trim()
1573
+ ? agentConfig.id.trim()
1574
+ : null,
1575
+ dialogId,
1576
+ currentSpaceId,
1577
+ userInput,
1578
+ runtimeContext,
1579
+ imageUrls: collectLatestImageUrls(messages),
1580
+ });
1581
+ toolExecutionMs += Math.max(0, Date.now() - toolStartedAt);
1582
+ if (firstToolResultAt === undefined) {
1583
+ firstToolResultAt = Date.now();
1584
+ }
1585
+ messages.push({
1586
+ role: "tool",
1587
+ tool_call_id: tc.id,
1588
+ content: toolResult,
1589
+ });
1590
+ const directCompletion = buildDirectCompletionFromToolResult(tc.function.name, toolResult);
1591
+ if (directCompletion) {
1592
+ finalContent = directCompletion;
1593
+ messages.push({
1594
+ role: "assistant",
1595
+ content: directCompletion,
1596
+ });
1597
+ return {
1598
+ content: finalContent,
1599
+ model: lastResolvedModel,
1600
+ provider: lastResolvedProvider,
1601
+ inputPrice: lastResolvedInputPrice,
1602
+ outputPrice: lastResolvedOutputPrice,
1603
+ usage: lastUsage,
1604
+ trace: messages.slice(traceStartIndex),
1605
+ runtimeToolNames,
1606
+ toolCallCount,
1607
+ policyState: {
1608
+ autoKnowledgeCaptureCount:
1609
+ (runtimePolicy?.knowledgeCapture.currentAutoCreates ?? 0) +
1610
+ runtimeState.autoKnowledgeCaptureCountThisRun,
1611
+ autoSpaceReadCount:
1612
+ (runtimePolicy?.spaceContext.currentAutoReads ?? 0) +
1613
+ runtimeState.autoSpaceReadCountThisRun,
1614
+ updatedAt: Date.now(),
1615
+ },
1616
+ latencyProfile: buildLatencyProfile({
1617
+ startedAt: startTime,
1618
+ llmRequestCount,
1619
+ llmWaitMs,
1620
+ llmJsonParseMs,
1621
+ toolExecutionMs,
1622
+ firstAssistantAt,
1623
+ firstToolResultAt,
1624
+ }),
1625
+ };
1626
+ }
1627
+ if (["runStreamingAgent", "streamParallelAgents"].includes(canonicalizeToolName(tc.function.name))) {
1628
+ const parsed = parseToolResult(toolResult);
1629
+ if (canonicalizeToolName(tc.function.name) === "streamParallelAgents") {
1630
+ appendParallelBranchMessages(messages, parsed);
1631
+ }
1632
+ if (parsed?.handoff && typeof parsed.content === "string" && parsed.content.trim()) {
1633
+ finalContent = parsed.content;
1634
+ if (canonicalizeToolName(tc.function.name) !== "streamParallelAgents") {
1635
+ messages.push({
1636
+ role: "assistant",
1637
+ content: parsed.content,
1638
+ });
1639
+ }
1640
+ return {
1641
+ content: finalContent,
1642
+ model: parsed.model || lastResolvedModel,
1643
+ provider: parsed.provider || lastResolvedProvider,
1644
+ inputPrice:
1645
+ typeof parsed.inputPrice === "number"
1646
+ ? parsed.inputPrice
1647
+ : lastResolvedInputPrice,
1648
+ outputPrice:
1649
+ typeof parsed.outputPrice === "number"
1650
+ ? parsed.outputPrice
1651
+ : lastResolvedOutputPrice,
1652
+ usage: parsed.usage ?? lastUsage,
1653
+ trace: messages.slice(traceStartIndex),
1654
+ runtimeToolNames,
1655
+ toolCallCount,
1656
+ policyState: {
1657
+ autoKnowledgeCaptureCount:
1658
+ (runtimePolicy?.knowledgeCapture.currentAutoCreates ?? 0) +
1659
+ runtimeState.autoKnowledgeCaptureCountThisRun,
1660
+ autoSpaceReadCount:
1661
+ (runtimePolicy?.spaceContext.currentAutoReads ?? 0) +
1662
+ runtimeState.autoSpaceReadCountThisRun,
1663
+ updatedAt: Date.now(),
1664
+ },
1665
+ latencyProfile: buildLatencyProfile({
1666
+ startedAt: startTime,
1667
+ llmRequestCount,
1668
+ llmWaitMs,
1669
+ llmJsonParseMs,
1670
+ toolExecutionMs,
1671
+ firstAssistantAt,
1672
+ firstToolResultAt,
1673
+ }),
1674
+ };
1675
+ }
1676
+ }
1677
+ }
1678
+ }
1679
+
1680
+ return {
1681
+ content: finalContent,
1682
+ model: lastResolvedModel,
1683
+ provider: lastResolvedProvider,
1684
+ inputPrice: lastResolvedInputPrice,
1685
+ outputPrice: lastResolvedOutputPrice,
1686
+ usage: lastUsage,
1687
+ trace: messages.slice(traceStartIndex),
1688
+ runtimeToolNames,
1689
+ toolCallCount,
1690
+ policyState: {
1691
+ autoKnowledgeCaptureCount:
1692
+ (runtimePolicy?.knowledgeCapture.currentAutoCreates ?? 0) +
1693
+ runtimeState.autoKnowledgeCaptureCountThisRun,
1694
+ autoSpaceReadCount:
1695
+ (runtimePolicy?.spaceContext.currentAutoReads ?? 0) +
1696
+ runtimeState.autoSpaceReadCountThisRun,
1697
+ updatedAt: Date.now(),
1698
+ },
1699
+ latencyProfile: buildLatencyProfile({
1700
+ startedAt: startTime,
1701
+ llmRequestCount,
1702
+ llmWaitMs,
1703
+ llmJsonParseMs,
1704
+ toolExecutionMs,
1705
+ firstAssistantAt,
1706
+ firstToolResultAt,
1707
+ }),
1708
+ };
1709
+ }
1710
+
1711
+ // ── 流式(SSE)────────────────────────────────────────────────────────────────
1712
+
1713
+ type Send = (data: object) => void;
1714
+
1715
+ export function buildSseStream(
1716
+ agentConfig: any,
1717
+ initialMessages: ChatMessage[],
1718
+ _maxRounds: number,
1719
+ serverBase: string,
1720
+ authToken: string,
1721
+ runtimePolicy?: ResolvedRuntimePolicy,
1722
+ userId?: string | null,
1723
+ dialogId?: string | null,
1724
+ currentSpaceId?: string | null,
1725
+ userInput?: string | null,
1726
+ runtimeContext?: AgentRunRuntimeContext | null,
1727
+ ): ReadableStream {
1728
+ const encoder = new TextEncoder();
1729
+
1730
+ return new ReadableStream({
1731
+ async start(controller) {
1732
+ const send: Send = (data) =>
1733
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`));
1734
+
1735
+ try {
1736
+ if (agentConfig.apiSource === "cli") {
1737
+ // CLI 流式输出仅依赖 provider 自己的 stdout 语义,不合成本地 tool 事件。
1738
+ const provider: CliProvider = (agentConfig.cliProvider ?? "copilot") as CliProvider;
1739
+ const taskPrompt = buildCliTaskPrompt(initialMessages, userInput);
1740
+ const fullPrompt = buildCliPrompt(agentConfig.prompt, taskPrompt);
1741
+
1742
+ const result = await executeCliStreaming(provider, fullPrompt, {
1743
+ model: agentConfig.model || undefined,
1744
+ yolo: true,
1745
+ onChunk: (chunk: string) => {
1746
+ if (!chunk) return;
1747
+ send({ type: "text", content: chunk });
1748
+ },
1749
+ });
1750
+
1751
+ send({ type: "done", usage: undefined, elapsed: result.elapsed });
1752
+ controller.close();
1753
+ return;
1754
+ }
1755
+
1756
+ const endpoint = getApiEndpoint(agentConfig);
1757
+ const tools = buildTools(agentConfig);
1758
+ // maxRounds 仅为兼容旧请求字段保留;工具轮次不再做硬限制。
1759
+ // 后续如需控制,应改为基于成本/预算的策略,而不是轮次数量。
1760
+ void _maxRounds;
1761
+ const messages: ChatMessage[] = [...initialMessages];
1762
+ const runtimeState: ToolPolicyRuntimeState = {
1763
+ autoKnowledgeCaptureCountThisRun: 0,
1764
+ autoSpaceReadCountThisRun: 0,
1765
+ };
1766
+ const startTime = Date.now();
1767
+ let lastUsage: Record<string, any> | undefined;
1768
+ let lastResolvedProvider = agentConfig.provider ?? undefined;
1769
+ let lastResolvedModel = agentConfig.model ?? "unknown";
1770
+ let lastResolvedInputPrice = agentConfig.inputPrice;
1771
+ let lastResolvedOutputPrice = agentConfig.outputPrice;
1772
+ let finalParallelSummaryRepairPending = false;
1773
+ let finalParallelSummaryRepairUsed = false;
1774
+ let emptyAssistantRepairPending = false;
1775
+ let emptyAssistantRepairUsed = false;
1776
+
1777
+ await runWereadGatewayPreflightIfNeeded({
1778
+ agentConfig,
1779
+ tools,
1780
+ messages,
1781
+ serverBase,
1782
+ authToken,
1783
+ runtimePolicy,
1784
+ runtimeState,
1785
+ userId,
1786
+ dialogId,
1787
+ currentSpaceId,
1788
+ userInput,
1789
+ runtimeContext,
1790
+ send,
1791
+ });
1792
+
1793
+ for (;;) {
1794
+ if (Date.now() - startTime > MAX_TIME_MS) {
1795
+ send({ type: "error", message: "Max execution time exceeded" });
1796
+ break;
1797
+ }
1798
+
1799
+ const requestMessages = finalParallelSummaryRepairPending
1800
+ ? [
1801
+ ...messages,
1802
+ { role: "system", content: FINAL_PARALLEL_SUMMARY_REPAIR_PROMPT } satisfies ChatMessage,
1803
+ ]
1804
+ : emptyAssistantRepairPending
1805
+ ? [
1806
+ ...messages,
1807
+ { role: "system", content: EMPTY_ASSISTANT_REPAIR_PROMPT } satisfies ChatMessage,
1808
+ ]
1809
+ : messages;
1810
+ const body = buildLoopRequestBody(agentConfig, requestMessages, tools, true);
1811
+ finalParallelSummaryRepairPending = false;
1812
+ emptyAssistantRepairPending = false;
1813
+
1814
+ let res: Response;
1815
+ try {
1816
+ const upstreamResult = await fetchLoopUpstream(endpoint, agentConfig, body);
1817
+ res = upstreamResult.response;
1818
+ lastResolvedProvider = upstreamResult.provider ?? lastResolvedProvider;
1819
+ lastResolvedModel = upstreamResult.model ?? lastResolvedModel;
1820
+ lastResolvedInputPrice = upstreamResult.inputPrice ?? lastResolvedInputPrice;
1821
+ lastResolvedOutputPrice = upstreamResult.outputPrice ?? lastResolvedOutputPrice;
1822
+ } catch (error: any) {
1823
+ send({ type: "error", message: error?.message ?? "Unexpected error" });
1824
+ break;
1825
+ }
1826
+ if (!res.ok || !res.body) {
1827
+ send({ type: "error", message: `LLM API error (${res.status}): ${await res.text()}` });
1828
+ break;
1829
+ }
1830
+
1831
+ const streamResult = isResponseAPIModel(agentConfig)
1832
+ ? await consumeResponsesSseStream(res.body, send)
1833
+ : await consumeSseStream(res.body, send, {
1834
+ suppressPreToolText: shouldSuppressPreToolText(agentConfig),
1835
+ });
1836
+ const {
1837
+ toolCalls,
1838
+ hasToolCalls,
1839
+ assistantContent,
1840
+ reasoningContent,
1841
+ } = streamResult;
1842
+ lastUsage = streamResult.lastUsage ?? lastUsage;
1843
+
1844
+ const assistantMsg: ChatMessage = {
1845
+ role: "assistant",
1846
+ content: assistantContent || null,
1847
+ ...(reasoningContent ? { reasoning_content: reasoningContent } : {}),
1848
+ ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
1849
+ };
1850
+ if (
1851
+ !hasToolCalls &&
1852
+ !extractAssistantText(assistantMsg).trim() &&
1853
+ !finalParallelSummaryRepairUsed &&
1854
+ hasParallelBranchMessages(messages)
1855
+ ) {
1856
+ finalParallelSummaryRepairPending = true;
1857
+ finalParallelSummaryRepairUsed = true;
1858
+ continue;
1859
+ }
1860
+ if (
1861
+ !hasToolCalls &&
1862
+ !extractAssistantText(assistantMsg).trim() &&
1863
+ !emptyAssistantRepairUsed
1864
+ ) {
1865
+ emptyAssistantRepairPending = true;
1866
+ emptyAssistantRepairUsed = true;
1867
+ continue;
1868
+ }
1869
+ messages.push(assistantMsg);
1870
+
1871
+ if (!hasToolCalls) {
1872
+ send({
1873
+ type: "done",
1874
+ usage: lastUsage,
1875
+ provider: lastResolvedProvider,
1876
+ model: lastResolvedModel,
1877
+ inputPrice: lastResolvedInputPrice,
1878
+ outputPrice: lastResolvedOutputPrice,
1879
+ });
1880
+ break;
1881
+ }
1882
+
1883
+ send({
1884
+ type: "assistant_tool_calls",
1885
+ content: assistantMsg.content ?? null,
1886
+ tool_calls: toolCalls,
1887
+ });
1888
+ send({ type: "tool_start", calls: toolCalls.map((t) => t.function.name) });
1889
+ for (const tc of toolCalls) {
1890
+ const toolResult = await executeToolOnServer(tc, serverBase, authToken, {
1891
+ policy: runtimePolicy,
1892
+ runtimeState,
1893
+ userId,
1894
+ agentKey:
1895
+ typeof agentConfig?.dbKey === "string" && agentConfig.dbKey.trim()
1896
+ ? agentConfig.dbKey.trim()
1897
+ : typeof agentConfig?.id === "string" && agentConfig.id.trim()
1898
+ ? agentConfig.id.trim()
1899
+ : null,
1900
+ dialogId,
1901
+ currentSpaceId,
1902
+ userInput,
1903
+ runtimeContext,
1904
+ imageUrls: collectLatestImageUrls(messages),
1905
+ });
1906
+ messages.push({
1907
+ role: "tool",
1908
+ tool_call_id: tc.id,
1909
+ content: toolResult,
1910
+ });
1911
+ send({
1912
+ type: "tool_result",
1913
+ toolCallId: tc.id,
1914
+ toolName: canonicalizeToolName(tc.function.name),
1915
+ content: toolResult,
1916
+ });
1917
+ const directCompletion = buildDirectCompletionFromToolResult(tc.function.name, toolResult);
1918
+ if (directCompletion) {
1919
+ messages.push({
1920
+ role: "assistant",
1921
+ content: directCompletion,
1922
+ });
1923
+ send({ type: "text", content: directCompletion });
1924
+ send({
1925
+ type: "done",
1926
+ usage: lastUsage,
1927
+ provider: lastResolvedProvider,
1928
+ model: lastResolvedModel,
1929
+ inputPrice: lastResolvedInputPrice,
1930
+ outputPrice: lastResolvedOutputPrice,
1931
+ });
1932
+ controller.close();
1933
+ return;
1934
+ }
1935
+ if (["runStreamingAgent", "streamParallelAgents"].includes(canonicalizeToolName(tc.function.name))) {
1936
+ const parsed = parseToolResult(toolResult);
1937
+ if (canonicalizeToolName(tc.function.name) === "streamParallelAgents") {
1938
+ appendParallelBranchMessages(messages, parsed);
1939
+ }
1940
+ if (parsed?.handoff && typeof parsed.content === "string" && parsed.content.trim()) {
1941
+ send({ type: "text", content: parsed.content });
1942
+ send({
1943
+ type: "done",
1944
+ usage: parsed.usage ?? lastUsage,
1945
+ provider: parsed.provider || lastResolvedProvider,
1946
+ model: parsed.model || lastResolvedModel,
1947
+ inputPrice:
1948
+ typeof parsed.inputPrice === "number"
1949
+ ? parsed.inputPrice
1950
+ : lastResolvedInputPrice,
1951
+ outputPrice:
1952
+ typeof parsed.outputPrice === "number"
1953
+ ? parsed.outputPrice
1954
+ : lastResolvedOutputPrice,
1955
+ });
1956
+ controller.close();
1957
+ return;
1958
+ }
1959
+ }
1960
+ // 如果是创建文档类工具,解析结果并推送 doc_created 事件让前端感知
1961
+ if (["createDoc", "createSkillDoc", "importSkill"].includes(canonicalizeToolName(tc.function.name))) {
1962
+ try {
1963
+ const parsed = JSON.parse(toolResult);
1964
+ if (parsed?.dbKey) {
1965
+ send({ type: "doc_created", dbKey: parsed.dbKey, title: parsed.title, spaceId: parsed.spaceId });
1966
+ }
1967
+ } catch { /* 忽略解析失败 */ }
1968
+ }
1969
+ }
1970
+ send({ type: "tool_end" });
1971
+ }
1972
+ } catch (e: any) {
1973
+ controller.enqueue(
1974
+ encoder.encode(`data: ${JSON.stringify({ type: "error", message: e.message ?? "Unexpected error" })}\n\n`)
1975
+ );
1976
+ }
1977
+ controller.close();
1978
+ },
1979
+ });
1980
+ }
1981
+
1982
+ interface SseConsumeResult {
1983
+ toolCalls: AssistantToolCall[];
1984
+ hasToolCalls: boolean;
1985
+ assistantContent: ChatMessage["content"];
1986
+ reasoningContent: string;
1987
+ lastUsage: any;
1988
+ }
1989
+
1990
+ function shouldSuppressPreToolText(agentConfig: any): boolean {
1991
+ return (
1992
+ agentConfig?.dbKey === BUILTIN_NOLO_AGENT_KEY ||
1993
+ agentConfig?.agentKey === BUILTIN_NOLO_AGENT_KEY ||
1994
+ agentConfig?.id === BUILTIN_NOLO_AGENT_KEY ||
1995
+ agentConfig?.id === "01NOLOAPPBLD000000019KCKT0"
1996
+ );
1997
+ }
1998
+
1999
+ async function consumeSseStream(
2000
+ body: ReadableStream<Uint8Array>,
2001
+ send: Send,
2002
+ options?: { suppressPreToolText?: boolean },
2003
+ ): Promise<SseConsumeResult> {
2004
+ const reader = body.getReader();
2005
+ const decoder = new TextDecoder();
2006
+ let buf = "";
2007
+ const acc: Record<number, AssistantToolCall> = {};
2008
+ let hasToolCalls = false;
2009
+ const assistantContentParts: Array<
2010
+ { type: "text"; text: string } | { type: "image_url"; image_url: { url: string } }
2011
+ > = [];
2012
+ let reasoningContent = "";
2013
+ let lastUsage: any = null;
2014
+ let suppressedText = "";
2015
+
2016
+ outer: while (true) {
2017
+ const { done, value } = await reader.read();
2018
+ if (done) break;
2019
+ buf += decoder.decode(value, { stream: true });
2020
+ const lines = buf.split("\n");
2021
+ buf = lines.pop() ?? "";
2022
+
2023
+ for (const line of lines) {
2024
+ if (!line.startsWith("data: ")) continue;
2025
+ const raw = line.slice(6).trim();
2026
+ if (raw === "[DONE]") break outer;
2027
+
2028
+ let chunk: any;
2029
+ try { chunk = JSON.parse(raw); } catch { continue; }
2030
+
2031
+ const delta = chunk.choices?.[0]?.delta;
2032
+ if (!delta) { if (chunk.usage) lastUsage = chunk.usage; continue; }
2033
+
2034
+ if (typeof delta.content === "string" && delta.content) {
2035
+ appendTextPart(assistantContentParts, delta.content);
2036
+ if (options?.suppressPreToolText) {
2037
+ suppressedText += delta.content;
2038
+ } else {
2039
+ send({ type: "text", content: delta.content });
2040
+ }
2041
+ }
2042
+
2043
+ if (Array.isArray(delta.images) && delta.images.length > 0) {
2044
+ assistantContentParts.push(...delta.images);
2045
+ }
2046
+
2047
+ // 思考内容:Ollama/Qwen3 用 delta.reasoning,DeepSeek 用 delta.reasoning_content
2048
+ const thinkChunk: string = delta.reasoning ?? delta.reasoning_content ?? "";
2049
+ if (thinkChunk) {
2050
+ reasoningContent += thinkChunk;
2051
+ send({ type: "thinking", content: thinkChunk });
2052
+ }
2053
+
2054
+ if (Array.isArray(delta.tool_calls)) {
2055
+ hasToolCalls = true;
2056
+ for (const tc of delta.tool_calls) {
2057
+ const idx: number = tc.index ?? 0;
2058
+ if (!acc[idx]) {
2059
+ acc[idx] = { id: tc.id ?? "", type: "function", function: { name: tc.function?.name ?? "", arguments: "" } };
2060
+ }
2061
+ acc[idx].function.arguments += tc.function?.arguments ?? "";
2062
+ if (tc.id) acc[idx].id = tc.id;
2063
+ if (tc.function?.name) acc[idx].function.name = tc.function.name;
2064
+ }
2065
+ }
2066
+
2067
+ if (chunk.usage) lastUsage = chunk.usage;
2068
+ }
2069
+ }
2070
+
2071
+ const assistantContent = finalizeAssistantContent(assistantContentParts);
2072
+ if (options?.suppressPreToolText && !hasToolCalls && suppressedText) {
2073
+ send({ type: "text", content: suppressedText });
2074
+ }
2075
+
2076
+ return {
2077
+ toolCalls: Object.values(acc),
2078
+ hasToolCalls,
2079
+ assistantContent,
2080
+ reasoningContent,
2081
+ lastUsage,
2082
+ };
2083
+ }
2084
+
2085
+ async function consumeResponsesSseStream(
2086
+ body: ReadableStream<Uint8Array>,
2087
+ send: Send
2088
+ ): Promise<SseConsumeResult> {
2089
+ const reader = body.getReader();
2090
+ const decoder = new TextDecoder();
2091
+ let buf = "";
2092
+ const acc = new Map<string, AssistantToolCall>();
2093
+ let hasToolCalls = false;
2094
+ let assistantContent = "";
2095
+ let reasoningContent = "";
2096
+ let lastUsage: any = null;
2097
+ let completedResponse: any = null;
2098
+
2099
+ outer: while (true) {
2100
+ const { done, value } = await reader.read();
2101
+ if (done) break;
2102
+ buf += decoder.decode(value, { stream: true });
2103
+ const lines = buf.split("\n");
2104
+ buf = lines.pop() ?? "";
2105
+
2106
+ for (const line of lines) {
2107
+ if (!line.startsWith("data: ")) continue;
2108
+ const raw = line.slice(6).trim();
2109
+ if (raw === "[DONE]") break outer;
2110
+
2111
+ let event: any;
2112
+ try {
2113
+ event = JSON.parse(raw);
2114
+ } catch {
2115
+ continue;
2116
+ }
2117
+
2118
+ if (event?.usage) lastUsage = event.usage;
2119
+
2120
+ switch (event?.type) {
2121
+ case "response.output_text.delta":
2122
+ if (event.delta) {
2123
+ assistantContent += event.delta;
2124
+ send({ type: "text", content: event.delta });
2125
+ }
2126
+ break;
2127
+ case "response.reasoning.delta":
2128
+ if (event.delta?.text) {
2129
+ reasoningContent += event.delta.text;
2130
+ send({ type: "thinking", content: event.delta.text });
2131
+ }
2132
+ break;
2133
+ case "response.output_item.added":
2134
+ case "response.output_item.done": {
2135
+ const item = event.item;
2136
+ if (!assistantContent) {
2137
+ const itemText = extractTextFromResponseOutputItem(item);
2138
+ if (itemText) {
2139
+ assistantContent = itemText;
2140
+ send({ type: "text", content: itemText });
2141
+ }
2142
+ }
2143
+ if (item?.type !== "function_call") break;
2144
+ hasToolCalls = true;
2145
+ const key = item.call_id || item.id;
2146
+ if (!key) break;
2147
+ acc.set(key, {
2148
+ id: item.call_id || item.id,
2149
+ type: "function",
2150
+ function: {
2151
+ name: item.name ?? "",
2152
+ arguments:
2153
+ typeof item.arguments === "string" ? item.arguments : "",
2154
+ },
2155
+ });
2156
+ break;
2157
+ }
2158
+ case "response.function_call_arguments.delta": {
2159
+ hasToolCalls = true;
2160
+ const key = event.call_id || event.item_id || `${event.output_index ?? 0}`;
2161
+ const current = acc.get(key) ?? {
2162
+ id: event.call_id || key,
2163
+ type: "function" as const,
2164
+ function: { name: event.name ?? "", arguments: "" },
2165
+ };
2166
+ current.function.arguments += event.delta ?? "";
2167
+ if (event.name) current.function.name = event.name;
2168
+ acc.set(key, current);
2169
+ break;
2170
+ }
2171
+ case "response.function_call_arguments.done": {
2172
+ hasToolCalls = true;
2173
+ const key = event.call_id || event.item_id || `${event.output_index ?? 0}`;
2174
+ const current = acc.get(key) ?? {
2175
+ id: event.call_id || key,
2176
+ type: "function" as const,
2177
+ function: { name: event.name ?? "", arguments: "" },
2178
+ };
2179
+ if (event.name) current.function.name = event.name;
2180
+ if (typeof event.arguments === "string") {
2181
+ current.function.arguments = event.arguments;
2182
+ }
2183
+ acc.set(key, current);
2184
+ break;
2185
+ }
2186
+ case "response.completed":
2187
+ completedResponse = event.response ?? null;
2188
+ if (event.response?.usage) lastUsage = event.response.usage;
2189
+ break;
2190
+ default:
2191
+ break;
2192
+ }
2193
+ }
2194
+ }
2195
+
2196
+ if (!assistantContent && completedResponse) {
2197
+ assistantContent = extractTextFromResponseOutput(completedResponse);
2198
+ if (assistantContent) {
2199
+ send({ type: "text", content: assistantContent });
2200
+ }
2201
+ }
2202
+
2203
+ return {
2204
+ toolCalls: Array.from(acc.values()),
2205
+ hasToolCalls,
2206
+ assistantContent,
2207
+ reasoningContent,
2208
+ lastUsage,
2209
+ };
2210
+ }