nolo-cli 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/README.md +9 -2
  2. package/agent-runtime/hostAdapter.ts +53 -0
  3. package/agent-runtime/index.ts +28 -0
  4. package/agent-runtime/localLoop.ts +62 -0
  5. package/agent-runtime/runtimeDecision.ts +70 -0
  6. package/agent-runtime/types.ts +87 -0
  7. package/agentRuntimeCommands.ts +139 -22
  8. package/agentRuntimeLocal.ts +7 -0
  9. package/ai/agent/_executeModel.ts +118 -0
  10. package/ai/agent/agentSlice.ts +544 -1
  11. package/ai/agent/appWorkingMemory.ts +126 -0
  12. package/ai/agent/avatarUtils.ts +24 -0
  13. package/ai/agent/buildEditingContext.ts +373 -0
  14. package/ai/agent/buildSystemPrompt.ts +532 -0
  15. package/ai/agent/cleanAgentMessages.ts +140 -0
  16. package/ai/agent/cliChatClient.ts +119 -0
  17. package/ai/agent/contextCompiler.ts +107 -0
  18. package/ai/agent/contextLayerContract.ts +44 -0
  19. package/ai/agent/createAgentSchema.ts +234 -0
  20. package/ai/agent/executeToolCall.ts +58 -0
  21. package/ai/agent/fetchAgentContexts.ts +42 -0
  22. package/ai/agent/generatePrompt.ts +3 -0
  23. package/ai/agent/getFullChatContextKeys.ts +168 -0
  24. package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
  25. package/ai/agent/hooks/useAgentConfig.ts +61 -0
  26. package/ai/agent/hooks/useAgentDialog.ts +35 -0
  27. package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
  28. package/ai/agent/hooks/usePublicAgents.ts +473 -0
  29. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  30. package/ai/agent/planSlice.ts +259 -0
  31. package/ai/agent/referenceUtils.ts +229 -0
  32. package/ai/agent/runAgentBackground.ts +238 -0
  33. package/ai/agent/runAgentClientLoop.ts +138 -0
  34. package/ai/agent/runtimeGuidance.ts +97 -0
  35. package/ai/agent/runtimeServerBase.ts +37 -0
  36. package/ai/agent/server/fetchPublicAgents.ts +128 -0
  37. package/ai/agent/startParallelAgentStreams.ts +424 -0
  38. package/ai/agent/startupProtocol.ts +53 -0
  39. package/ai/agent/streamAgentChatTurn.ts +1299 -0
  40. package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
  41. package/ai/agent/types.ts +71 -0
  42. package/ai/agent/utils/imageOutput.ts +39 -0
  43. package/ai/agent/utils/publicImageAgentMode.ts +26 -0
  44. package/ai/agent/utils/sortUtils.ts +250 -0
  45. package/ai/agent/web/referencePickerUtils.ts +146 -0
  46. package/ai/ai.locale.ts +1083 -0
  47. package/ai/chat/accumulateToolCallChunks.ts +95 -0
  48. package/ai/chat/fetchUtils.native.ts +276 -0
  49. package/ai/chat/fetchUtils.ts +153 -0
  50. package/ai/chat/inlineImageUrlsForCustomProvider.ts +117 -0
  51. package/ai/chat/parseApiError.ts +64 -0
  52. package/ai/chat/parseMultilineSSE.ts +95 -0
  53. package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
  54. package/ai/chat/sendOpenAICompletionsRequest.ts +712 -0
  55. package/ai/chat/sendOpenAIResponseRequest.ts +512 -0
  56. package/ai/chat/shouldUseServerProxy.ts +18 -0
  57. package/ai/chat/sseClient.native.ts +91 -0
  58. package/ai/chat/sseClient.ts +67 -0
  59. package/ai/chat/streamReader.native.ts +31 -0
  60. package/ai/chat/streamReader.ts +62 -0
  61. package/ai/chat/updateTotalUsage.ts +72 -0
  62. package/ai/context/buildReferenceContext.ts +437 -0
  63. package/ai/context/calculateContextUsage.ts +133 -0
  64. package/ai/context/retention.ts +165 -0
  65. package/ai/context/tokenUtils.ts +78 -0
  66. package/ai/index.ts +1 -1
  67. package/ai/llm/agentCapabilities.ts +74 -0
  68. package/ai/llm/calculateGeminiImageTokens.ts +57 -0
  69. package/ai/llm/deepinfra.ts +28 -0
  70. package/ai/llm/fireworks.ts +68 -0
  71. package/ai/llm/generateRequestBody.ts +165 -0
  72. package/ai/llm/getModelContextWindow.ts +84 -0
  73. package/ai/llm/getNoloKey.ts +37 -0
  74. package/ai/llm/getPricing.ts +232 -0
  75. package/ai/llm/hooks/useModelPricing.ts +75 -0
  76. package/ai/llm/imagePricing.ts +66 -0
  77. package/ai/llm/isResponseAPIModel.ts +13 -0
  78. package/ai/llm/kimi.ts +18 -0
  79. package/ai/llm/mimo.ts +71 -0
  80. package/ai/llm/mistral.ts +22 -0
  81. package/ai/llm/modelAvatar.ts +427 -0
  82. package/ai/llm/models.ts +45 -0
  83. package/ai/llm/openrouterModels.ts +141 -0
  84. package/ai/llm/providers.ts +307 -0
  85. package/ai/llm/reasoningModels.ts +28 -0
  86. package/ai/llm/types.ts +59 -0
  87. package/ai/llm/usageRequestOptions.ts +59 -0
  88. package/ai/memory/capture.ts +148 -0
  89. package/ai/memory/consolidate.ts +104 -0
  90. package/ai/memory/delete.ts +147 -0
  91. package/ai/memory/overlay.ts +84 -0
  92. package/ai/memory/query.ts +38 -0
  93. package/ai/memory/queryShared.ts +160 -0
  94. package/ai/memory/rank.ts +105 -0
  95. package/ai/memory/recentRelationshipRecap.ts +247 -0
  96. package/ai/memory/remember.ts +167 -0
  97. package/ai/memory/runtime.ts +76 -0
  98. package/ai/memory/store.ts +20 -0
  99. package/ai/memory/storeShared.ts +76 -0
  100. package/ai/memory/types.ts +46 -0
  101. package/ai/memory/understanding.ts +349 -0
  102. package/ai/memory/understandingGreeting.ts +264 -0
  103. package/ai/messages/type.ts +20 -0
  104. package/ai/policy/personalizationDialog.ts +333 -0
  105. package/ai/policy/runtimePolicy.ts +440 -0
  106. package/ai/policy/selfUpdateFields.ts +48 -0
  107. package/ai/policy/types.ts +64 -0
  108. package/ai/skills/referenceRuntime.ts +274 -0
  109. package/ai/skills/skillDiagnostics.ts +251 -0
  110. package/ai/skills/skillDocBuilder.ts +139 -0
  111. package/ai/skills/skillDocProtocol.ts +434 -0
  112. package/ai/skills/skillReferenceSummary.ts +63 -0
  113. package/ai/skills/skillSummaryMarker.ts +26 -0
  114. package/ai/token/calculatePrice.ts +546 -0
  115. package/ai/token/db.ts +98 -0
  116. package/ai/token/externalToolCost.ts +321 -0
  117. package/ai/token/hooks/useRecords.ts +65 -0
  118. package/ai/token/missingUsageEstimate.ts +42 -0
  119. package/ai/token/modelUsageQuery.ts +252 -0
  120. package/ai/token/normalizeUsage.ts +84 -0
  121. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  122. package/ai/token/prepareTokenUsageData.ts +88 -0
  123. package/ai/token/query.ts +88 -0
  124. package/ai/token/queryUserTokens.ts +59 -0
  125. package/ai/token/resolveBillingTarget.ts +52 -0
  126. package/ai/token/saveTokenRecord.ts +53 -0
  127. package/ai/token/serverDialogProjection.ts +78 -0
  128. package/ai/token/serverTokenWriter.ts +143 -0
  129. package/ai/token/stats.ts +21 -0
  130. package/ai/token/tokenThunks.ts +24 -0
  131. package/ai/token/types.ts +93 -0
  132. package/ai/tools/agent/agentTools.ts +176 -0
  133. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  134. package/ai/tools/agent/callAgentTool.ts +139 -0
  135. package/ai/tools/agent/createAgentTool.ts +512 -0
  136. package/ai/tools/agent/createDialogTool.ts +69 -0
  137. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  138. package/ai/tools/agent/parallelBudget.ts +221 -0
  139. package/ai/tools/agent/presets/appBuilderPreset.ts +147 -0
  140. package/ai/tools/agent/runLlmTool.ts +96 -0
  141. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  142. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  143. package/ai/tools/agent/skillAgentPreset.ts +89 -0
  144. package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
  145. package/ai/tools/agent/updateAgentTool.ts +96 -0
  146. package/ai/tools/agent/updateSelfTool.ts +113 -0
  147. package/ai/tools/amazonProductScraperTool.ts +86 -0
  148. package/ai/tools/apifyActorClient.ts +45 -0
  149. package/ai/tools/appEditGuard.ts +372 -0
  150. package/ai/tools/appReadSnapshot.ts +153 -0
  151. package/ai/tools/appTools.ts +1549 -0
  152. package/ai/tools/applyEditTool.ts +256 -0
  153. package/ai/tools/applyLineEditsTool.ts +312 -0
  154. package/ai/tools/browserTools/click.ts +33 -0
  155. package/ai/tools/browserTools/closeSession.ts +29 -0
  156. package/ai/tools/browserTools/common.ts +27 -0
  157. package/ai/tools/browserTools/openSession.ts +48 -0
  158. package/ai/tools/browserTools/readContent.ts +38 -0
  159. package/ai/tools/browserTools/selectOption.ts +46 -0
  160. package/ai/tools/browserTools/typeText.ts +42 -0
  161. package/ai/tools/category/createCategoryTool.ts +66 -0
  162. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  163. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  164. package/ai/tools/cfBrowserTools.ts +319 -0
  165. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  166. package/ai/tools/checkEnvTool.ts +65 -0
  167. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  168. package/ai/tools/codeSearchTool.ts +111 -0
  169. package/ai/tools/codeTools.ts +101 -0
  170. package/ai/tools/createDocTool.ts +132 -0
  171. package/ai/tools/createPlanTool.ts +999 -0
  172. package/ai/tools/createSkillDocTool.ts +155 -0
  173. package/ai/tools/createWorkflowTool.ts +154 -0
  174. package/ai/tools/deepseekOcrTool.ts +34 -0
  175. package/ai/tools/delayTool.ts +31 -0
  176. package/ai/tools/deleteSpacesTool.ts +325 -0
  177. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  178. package/ai/tools/devReloadUtils.ts +29 -0
  179. package/ai/tools/dialogMessageSearch.ts +137 -0
  180. package/ai/tools/doctorSkillTool.ts +72 -0
  181. package/ai/tools/ecommerceScraperTool.ts +86 -0
  182. package/ai/tools/emailTools.ts +549 -0
  183. package/ai/tools/evalSkillTool.ts +92 -0
  184. package/ai/tools/exaSearchTool.ts +64 -0
  185. package/ai/tools/execBashTool.ts +379 -0
  186. package/ai/tools/executeSqlTool.ts +192 -0
  187. package/ai/tools/fetchWebpageSupport.ts +309 -0
  188. package/ai/tools/fetchWebpageTool.ts +84 -0
  189. package/ai/tools/geminiImagePreviewTool.ts +361 -0
  190. package/ai/tools/generateDocxTool.ts +215 -0
  191. package/ai/tools/googleSearchScraperTool.ts +106 -0
  192. package/ai/tools/importDataTool.ts +133 -0
  193. package/ai/tools/importSkillTool.ts +162 -0
  194. package/ai/tools/index.ts +1927 -0
  195. package/ai/tools/listFilesTool.ts +82 -0
  196. package/ai/tools/listUserSpacesTool.ts +113 -0
  197. package/ai/tools/modelUsageTools.ts +199 -0
  198. package/ai/tools/olmOcrTool.ts +34 -0
  199. package/ai/tools/openaiImageTool.ts +267 -0
  200. package/ai/tools/prepareTools.ts +23 -0
  201. package/ai/tools/readDocTool.ts +84 -0
  202. package/ai/tools/readFileTool.ts +211 -0
  203. package/ai/tools/readTool.ts +163 -0
  204. package/ai/tools/readXPostTool.ts +233 -0
  205. package/ai/tools/rememberMemoryTool.ts +84 -0
  206. package/ai/tools/remotionVideoTool.ts +151 -0
  207. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  208. package/ai/tools/searchRepoTool.ts +115 -0
  209. package/ai/tools/searchWorkspaceTool.ts +259 -0
  210. package/ai/tools/skillFollowup.ts +86 -0
  211. package/ai/tools/surfWeatherTool.ts +169 -0
  212. package/ai/tools/table/addTableRowTool.ts +217 -0
  213. package/ai/tools/table/createTableTool.ts +315 -0
  214. package/ai/tools/table/rowTools.ts +366 -0
  215. package/ai/tools/table/schemaTools.ts +244 -0
  216. package/ai/tools/table/shareTableTool.ts +148 -0
  217. package/ai/tools/table/toolShared.ts +129 -0
  218. package/ai/tools/toolApiClient.ts +198 -0
  219. package/ai/tools/toolNameAliases.ts +57 -0
  220. package/ai/tools/toolResultError.ts +42 -0
  221. package/ai/tools/toolRunSlice.ts +303 -0
  222. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  223. package/ai/tools/toolVisibility.ts +4 -0
  224. package/ai/tools/types.ts +20 -0
  225. package/ai/tools/uiAskChoiceTool.ts +104 -0
  226. package/ai/tools/updateContentTitleTool.ts +84 -0
  227. package/ai/tools/updateDocTool.ts +105 -0
  228. package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
  229. package/ai/tools/whisperTool.ts +77 -0
  230. package/ai/tools/writeFileTool.ts +210 -0
  231. package/ai/tools/youtubeScraperTool.ts +116 -0
  232. package/ai/tools/ziweiChartTool.ts +678 -0
  233. package/ai/types.ts +55 -0
  234. package/ai/workflow/workflowExecutor.ts +323 -0
  235. package/ai/workflow/workflowSlice.ts +73 -0
  236. package/ai/workflow/workflowTypes.ts +106 -0
  237. package/client/agentRun.test.ts +240 -0
  238. package/client/agentRun.ts +182 -19
  239. package/client/compactDialog.test.ts +238 -0
  240. package/client/localRuntimeAdapter.test.ts +135 -0
  241. package/client/localRuntimeAdapter.ts +244 -0
  242. package/client/profileConfig.test.ts +40 -0
  243. package/client/streamingOutput.test.ts +22 -0
  244. package/client/streamingOutput.ts +38 -0
  245. package/commandRegistry.ts +9 -2
  246. package/connector-experimental/index.ts +5 -0
  247. package/database/actions/cacheMergedUserData.ts +64 -0
  248. package/database/actions/common.ts +242 -0
  249. package/database/actions/deleteFile.ts +40 -0
  250. package/database/actions/fetchUserData.ts +16 -0
  251. package/database/actions/fileContent.ts +125 -0
  252. package/database/actions/patch.ts +155 -0
  253. package/database/actions/read.ts +337 -0
  254. package/database/actions/readAndWait.ts +224 -0
  255. package/database/actions/readRequestManager.ts +120 -0
  256. package/database/actions/remove.ts +94 -0
  257. package/database/actions/replication.ts +366 -0
  258. package/database/actions/upload.ts +174 -0
  259. package/database/actions/upsert.ts +56 -0
  260. package/database/actions/write.ts +126 -0
  261. package/database/client/db.native.ts +73 -0
  262. package/database/client/db.ts +51 -0
  263. package/database/client/fetchUserData.ts +61 -0
  264. package/database/client/handleError.ts +19 -0
  265. package/database/client/queryRequest.ts +21 -0
  266. package/database/config.ts +21 -0
  267. package/database/dbActionThunks.ts +1 -0
  268. package/database/dbSlice.ts +149 -0
  269. package/database/email.ts +42 -0
  270. package/database/fileRing.ts +51 -0
  271. package/database/fileSharding.ts +70 -0
  272. package/database/fileStorage.native.ts +92 -0
  273. package/database/fileStorage.ts +232 -0
  274. package/database/fileUrl.ts +34 -0
  275. package/database/hooks/useUserData.ts +489 -0
  276. package/database/index.ts +1 -0
  277. package/database/keys.ts +765 -0
  278. package/database/queryPrefixes.ts +14 -0
  279. package/database/requests.ts +443 -0
  280. package/database/runtimeServerContext.ts +35 -0
  281. package/database/server/MemoryDB.ts +76 -0
  282. package/database/server/actorAccess.ts +76 -0
  283. package/database/server/agentDelegation.ts +124 -0
  284. package/database/server/coreDataOwnership.ts +13 -0
  285. package/database/server/coreDataProxy.ts +76 -0
  286. package/database/server/cybotReadonly.ts +18 -0
  287. package/database/server/dataHandlers.ts +111 -0
  288. package/database/server/db.ts +118 -0
  289. package/database/server/dbPath.ts +20 -0
  290. package/database/server/delete.ts +499 -0
  291. package/database/server/emailRepository.ts +1480 -0
  292. package/database/server/ensureDbOpen.ts +12 -0
  293. package/database/server/fileRead.ts +337 -0
  294. package/database/server/fileService.ts +436 -0
  295. package/database/server/handleTransaction.ts +86 -0
  296. package/database/server/patch.ts +282 -0
  297. package/database/server/query.ts +138 -0
  298. package/database/server/read.ts +325 -0
  299. package/database/server/resourceAccess.ts +211 -0
  300. package/database/server/routes.ts +110 -0
  301. package/database/server/spaceMemberAuthority.ts +67 -0
  302. package/database/server/upload.ts +159 -0
  303. package/database/server/write.ts +494 -0
  304. package/database/server/writeAuthority.ts +133 -0
  305. package/database/sqliteDb.ts +46 -0
  306. package/database/table/deleteTable.ts +120 -0
  307. package/database/tenantPlacement.ts +57 -0
  308. package/database/tombstones.ts +52 -0
  309. package/database/userDataLoadDecision.ts +17 -0
  310. package/database/userDataMerge.ts +95 -0
  311. package/database/userPreferenceRegister.ts +108 -0
  312. package/database/utils/dbPath.ts +47 -0
  313. package/database/utils/ulid.native.ts +6 -0
  314. package/database/utils/ulid.ts +1 -0
  315. package/index.ts +25 -15
  316. package/localRuntimeDb.ts +28 -0
  317. package/package.json +16 -4
  318. package/runtimeModeArgs.ts +33 -0
  319. package/tui/readlineWorkspace.ts +1 -0
  320. package/tui/session.ts +22 -0
@@ -0,0 +1,115 @@
1
+ // 文件路径: packages/ai/tools/searchRepoTool.ts
2
+
3
+ import { getToolBaseUrl } from "./toolApiClient";
4
+
5
+ // ---- Types ----
6
+
7
+ export type SearchRepoArgs = {
8
+ query: string;
9
+ pathScope?: string;
10
+ maxResults?: number;
11
+ contextLines?: number;
12
+ };
13
+
14
+ // ---- 工具 Schema ----
15
+
16
+ export const searchRepoFunctionSchema = {
17
+ name: "search_repo",
18
+ description: [
19
+ "在项目的源代码中进行全量文本搜索。",
20
+ "当你不知道某个特定的字符串、类名、变量或逻辑在哪个文件中时,请使用此工具。",
21
+ "它会返回匹配的文件路径、行号以及代码预览。",
22
+ "",
23
+ "参数建议:",
24
+ "- query: 要搜索的文本(不区分大小写)。",
25
+ "- pathScope: 可选,缩小搜索范围(如 'packages/server')。",
26
+ "- maxResults: 默认 20。",
27
+ ].join("\n"),
28
+ parameters: {
29
+ type: "object",
30
+ properties: {
31
+ query: {
32
+ type: "string",
33
+ description: "要搜索的文本字符串。",
34
+ },
35
+ pathScope: {
36
+ type: "string",
37
+ description: "可选:限制搜索在特定目录下,例如 'packages/ai'。",
38
+ },
39
+ maxResults: {
40
+ type: "number",
41
+ description: "返回的最大匹配项数量 (默认 20)。",
42
+ },
43
+ contextLines: {
44
+ type: "number",
45
+ description: "可选:匹配行前后显示的上下文行数。",
46
+ },
47
+ },
48
+ required: ["query"],
49
+ },
50
+ };
51
+
52
+ function buildSearchRepoDisplayData(
53
+ query: string,
54
+ hits: Array<{ path?: string; line?: number; preview?: string }>,
55
+ totalHits: number | string | undefined,
56
+ ): string {
57
+ if (!Array.isArray(hits) || hits.length === 0) {
58
+ return `🔍 搜索 "${query}": 未找到匹配项`;
59
+ }
60
+
61
+ const lines = hits.slice(0, 8).map((hit, index) => {
62
+ const path = hit.path || "unknown";
63
+ const line = typeof hit.line === "number" ? `:${hit.line}` : "";
64
+ const preview = typeof hit.preview === "string" && hit.preview.trim()
65
+ ? ` — ${hit.preview.trim()}`
66
+ : "";
67
+ return `${index + 1}. ${path}${line}${preview}`;
68
+ });
69
+
70
+ const omitted = hits.length > 8 ? `\n… 其余 ${hits.length - 8} 条已省略` : "";
71
+ return `🔍 搜索 "${query}": 找到 ${totalHits ?? hits.length} 个匹配项\n${lines.join("\n")}${omitted}`;
72
+ }
73
+
74
+ // ---- 执行函数 ----
75
+
76
+ export async function searchRepoFunc(
77
+ args: SearchRepoArgs,
78
+ thunkApi: any,
79
+ context?: { signal?: AbortSignal }
80
+ ): Promise<{ rawData: any; displayData?: string }> {
81
+ const { query, pathScope, maxResults, contextLines } = args;
82
+
83
+ try {
84
+ const baseUrl = getToolBaseUrl(thunkApi);
85
+ const apiUrl = `${baseUrl}/api/search-repo`;
86
+
87
+ const response = await fetch(apiUrl, {
88
+ method: "POST",
89
+ headers: { "Content-Type": "application/json" },
90
+ signal: context?.signal,
91
+ body: JSON.stringify({ query, pathScope, maxResults, contextLines }),
92
+ });
93
+
94
+ const data = (await response.json()) as { ok?: boolean; error?: string; hits?: any[]; totalHits?: number | string };
95
+
96
+ if (!response.ok || data?.error) {
97
+ throw new Error(data?.error || `搜索请求失败: ${response.status}`);
98
+ }
99
+
100
+ const hits = data.hits || [];
101
+ const displayData = buildSearchRepoDisplayData(query, hits, data.totalHits);
102
+
103
+ return {
104
+ rawData: {
105
+ ok: true,
106
+ query,
107
+ hits,
108
+ totalHits: data.totalHits,
109
+ },
110
+ displayData,
111
+ };
112
+ } catch (error: any) {
113
+ throw new Error(`全局搜索 (${query}) 失败:${error?.message || String(error)}`);
114
+ }
115
+ }
@@ -0,0 +1,259 @@
1
+ // ai/tools/searchWorkspaceTool.ts
2
+
3
+ import {
4
+ buildMyContentItemsFromUserData,
5
+ MY_CONTENT_USER_DATA_TYPES,
6
+ type MyContentListItem,
7
+ } from "app/utils/myContentItems";
8
+ import type { SpaceContent, SpaceData, SpaceMemberWithSpaceInfo } from "app/types";
9
+ import { fetchUserData } from "database/client/fetchUserData";
10
+ import { getUserDataItemTimestamp, mergeAndDedupUserData } from "database/userDataMerge";
11
+
12
+ type SearchWorkspaceArgs = { query: string };
13
+
14
+ type SearchWorkspaceResultItem = {
15
+ title: string;
16
+ type: string;
17
+ contentKey: string;
18
+ spaceId: string | null;
19
+ spaceName?: string;
20
+ createdAt?: string | number;
21
+ updatedAt?: string | number;
22
+ serverOrigin?: string;
23
+ };
24
+
25
+ type SearchWorkspaceResult = {
26
+ rawData: { success: true; contents: SearchWorkspaceResultItem[] };
27
+ displayData: string;
28
+ };
29
+
30
+ type SearchWorkspaceState = {
31
+ auth?: {
32
+ currentUser?: {
33
+ userId?: string | null;
34
+ } | null;
35
+ };
36
+ settings?: {
37
+ currentServer?: string;
38
+ };
39
+ space: {
40
+ currentSpaceId: string | null;
41
+ currentSpace: SpaceData | null;
42
+ memberSpaces: SpaceMemberWithSpaceInfo[] | null;
43
+ };
44
+ };
45
+
46
+ type SearchWorkspaceThunkApi = {
47
+ getState: () => SearchWorkspaceState;
48
+ dispatch: (action: unknown) => unknown;
49
+ extra: {
50
+ db: any | null;
51
+ tokenManager: any | null;
52
+ };
53
+ };
54
+
55
+ const selectCurrentSpaceIdFromState = (state: SearchWorkspaceState) =>
56
+ state.space.currentSpaceId;
57
+
58
+ const selectCurrentSpaceFromState = (state: SearchWorkspaceState) =>
59
+ state.space.currentSpace;
60
+
61
+ const selectAllMemberSpacesFromState = (state: SearchWorkspaceState) => {
62
+ const memberSpaces = state.space.memberSpaces ?? [];
63
+ return [...memberSpaces].sort((a, b) => {
64
+ const aUpdatedAt =
65
+ a.spaceUpdatedAt ??
66
+ (a as any).memberUpdatedAt ??
67
+ (a as any).updatedAt ??
68
+ (a as any).createdAt ??
69
+ a.joinedAt ??
70
+ 0;
71
+ const bUpdatedAt =
72
+ b.spaceUpdatedAt ??
73
+ (b as any).memberUpdatedAt ??
74
+ (b as any).updatedAt ??
75
+ (b as any).createdAt ??
76
+ b.joinedAt ??
77
+ 0;
78
+ return bUpdatedAt - aUpdatedAt;
79
+ });
80
+ };
81
+
82
+ export const searchWorkspaceFunctionSchema = {
83
+ name: "search_workspace",
84
+ description: "在当前空间(Workspace)中搜索页面、表格等内容。",
85
+ parameters: {
86
+ type: "object",
87
+ properties: {
88
+ query: {
89
+ type: "string",
90
+ description: "搜索关键词。将匹配标题和文件名。",
91
+ },
92
+ },
93
+ required: ["query"],
94
+ },
95
+ };
96
+
97
+ export const searchAllSpacesFunctionSchema = {
98
+ name: "search_all_spaces",
99
+ description:
100
+ "在当前设备已同步的全部内容中搜索当前用户的数据(等同于全部视图 Recent 的内容语义),并返回所属空间(如有)。",
101
+ parameters: searchWorkspaceFunctionSchema.parameters,
102
+ };
103
+
104
+ const normalizeQuery = (value: unknown) => String(value || "").trim().toLowerCase();
105
+
106
+ const toSearchResultItem = (
107
+ item:
108
+ | Pick<
109
+ MyContentListItem,
110
+ "title" | "type" | "contentKey" | "spaceId" | "spaceName" | "createdAt" | "updatedAt" | "serverOrigin"
111
+ >
112
+ | (SpaceContent & { spaceId: string; spaceName?: string })
113
+ ): SearchWorkspaceResultItem => ({
114
+ title: String(item.title || item.contentKey || "").trim(),
115
+ type: String(item.type || "").trim(),
116
+ contentKey: String(item.contentKey || "").trim(),
117
+ spaceId: item.spaceId ?? null,
118
+ ...(item.spaceName ? { spaceName: item.spaceName } : {}),
119
+ ...(item.createdAt !== undefined ? { createdAt: item.createdAt } : {}),
120
+ ...(item.updatedAt !== undefined ? { updatedAt: item.updatedAt } : {}),
121
+ ...(typeof item.serverOrigin === "string" && item.serverOrigin.trim().length > 0
122
+ ? { serverOrigin: item.serverOrigin }
123
+ : {}),
124
+ });
125
+
126
+ const searchSpaceContents = (
127
+ space: SpaceData,
128
+ query: string,
129
+ options?: { spaceName?: string }
130
+ ): SearchWorkspaceResultItem[] => {
131
+ const kw = normalizeQuery(query);
132
+ const allContents = Object.values(space.contents || {});
133
+
134
+ return allContents
135
+ .filter((content): content is SpaceContent => {
136
+ if (!content) return false;
137
+ const titleMatch = (content.title ?? "").toLowerCase().includes(kw);
138
+ const keyMatch = (content.contentKey ?? "").toLowerCase().includes(kw);
139
+ return titleMatch || keyMatch;
140
+ })
141
+ .map((content) =>
142
+ toSearchResultItem({
143
+ ...content,
144
+ spaceId: space.id,
145
+ ...(options?.spaceName ? { spaceName: options.spaceName } : {}),
146
+ })
147
+ );
148
+ };
149
+
150
+ const matchesMyContentSearch = (
151
+ query: string,
152
+ item: Pick<MyContentListItem, "title" | "type" | "spaceName" | "contentKey">
153
+ ) => {
154
+ const kw = normalizeQuery(query);
155
+ if (!kw) return false;
156
+ return [item.title, item.type, item.spaceName, item.contentKey].some((value) =>
157
+ normalizeQuery(value).includes(kw)
158
+ );
159
+ };
160
+
161
+ const buildScopedDisplayData = (
162
+ query: string,
163
+ results: SearchWorkspaceResultItem[],
164
+ scopeLabel: string
165
+ ) => {
166
+ if (results.length === 0) {
167
+ return `未能在${scopeLabel}中找到匹配 "${query}" 的内容。`;
168
+ }
169
+
170
+ const summary = results
171
+ .map((item) => {
172
+ const spaceSuffix = item.spaceName ? ` [${item.spaceName}]` : "";
173
+ return `- ${item.title} (${item.contentKey})${spaceSuffix}`;
174
+ })
175
+ .join("\n");
176
+
177
+ return `在${scopeLabel}中找到 ${results.length} 个匹配项:\n${summary}`;
178
+ };
179
+
180
+ const loadLocalMyContentResults = async (
181
+ thunkApi: SearchWorkspaceThunkApi,
182
+ state: SearchWorkspaceState
183
+ ): Promise<MyContentListItem[]> => {
184
+ const userId = state.auth?.currentUser?.userId?.trim();
185
+ if (!userId) {
186
+ throw new Error("无法搜索全部内容,因为当前用户未登录。");
187
+ }
188
+
189
+ const db = thunkApi.extra?.db;
190
+ if (!db) {
191
+ throw new Error("无法搜索全部内容,因为本地数据库不可用。");
192
+ }
193
+
194
+ const localResults = await fetchUserData(db, MY_CONTENT_USER_DATA_TYPES, userId, {
195
+ includeDeleted: true,
196
+ });
197
+ const dedupedRecords = mergeAndDedupUserData(Object.values(localResults).flat(), []);
198
+ const sortedRecords = [...dedupedRecords].sort(
199
+ (a, b) => getUserDataItemTimestamp(b) - getUserDataItemTimestamp(a)
200
+ );
201
+ const memberSpaces = selectAllMemberSpacesFromState(state);
202
+ const spaceNameById = new Map(
203
+ memberSpaces.map((space) => [space.spaceId, space.spaceName || space.spaceId] as const)
204
+ );
205
+
206
+ return buildMyContentItemsFromUserData(
207
+ sortedRecords,
208
+ state.settings?.currentServer || "",
209
+ spaceNameById,
210
+ "我的应用",
211
+ "我的内容"
212
+ );
213
+ };
214
+
215
+ /**
216
+ * [Executor] 'search_workspace' 工具的执行函数。
217
+ */
218
+ export async function searchWorkspaceFunc(
219
+ args: SearchWorkspaceArgs,
220
+ thunkApi: SearchWorkspaceThunkApi
221
+ ): Promise<SearchWorkspaceResult> {
222
+ const { getState } = thunkApi;
223
+ const state = getState();
224
+ const spaceId = selectCurrentSpaceIdFromState(state);
225
+
226
+ if (!spaceId) {
227
+ throw new Error("无法查询内容,因为当前空间未设定。");
228
+ }
229
+
230
+ const currentSpace = selectCurrentSpaceFromState(state);
231
+ if (!currentSpace) {
232
+ throw new Error("当前空间数据未找到。");
233
+ }
234
+
235
+ const results = searchSpaceContents(currentSpace, args.query, {
236
+ spaceName: currentSpace.name,
237
+ });
238
+
239
+ const rawData = { success: true, contents: results };
240
+ const displayData = buildScopedDisplayData(args.query, results, "当前空间");
241
+
242
+ return { rawData, displayData };
243
+ }
244
+
245
+ export async function searchAllSpacesFunc(
246
+ args: SearchWorkspaceArgs,
247
+ thunkApi: SearchWorkspaceThunkApi
248
+ ): Promise<SearchWorkspaceResult> {
249
+ const state = thunkApi.getState();
250
+ const items = await loadLocalMyContentResults(thunkApi, state);
251
+ const results = items
252
+ .filter((item) => matchesMyContentSearch(args.query, item))
253
+ .map((item) => toSearchResultItem(item));
254
+
255
+ return {
256
+ rawData: { success: true, contents: results },
257
+ displayData: buildScopedDisplayData(args.query, results, "已同步的全部内容"),
258
+ };
259
+ }
@@ -0,0 +1,86 @@
1
+ export type SkillReferenceChoice = {
2
+ dbKey: string;
3
+ title: string;
4
+ type: "instruction";
5
+ };
6
+
7
+ export type SkillFollowupChoice = {
8
+ id: "save_only" | "use_existing_agent" | "create_agent";
9
+ label: string;
10
+ userMessage: string;
11
+ };
12
+
13
+ type SkillFollowupInput = {
14
+ dbKey: string;
15
+ title: string;
16
+ skillId?: string;
17
+ spaceId?: string | null;
18
+ toolNames?: string[];
19
+ hasEvalConfig?: boolean;
20
+ importedFrom?: string | null;
21
+ };
22
+
23
+ export const buildSkillReferenceChoice = (
24
+ dbKey: string,
25
+ title: string
26
+ ): SkillReferenceChoice => ({
27
+ dbKey,
28
+ title,
29
+ type: "instruction",
30
+ });
31
+
32
+ export const buildSkillFollowupChoices = (
33
+ reference: SkillReferenceChoice
34
+ ): SkillFollowupChoice[] => [
35
+ {
36
+ id: "save_only",
37
+ label: "仅保存",
38
+ userMessage: `先只保存这个 skill 文档,不挂到任何 agent。skill dbKey 是 ${reference.dbKey}。`,
39
+ },
40
+ {
41
+ id: "use_existing_agent",
42
+ label: "挂到现有 Agent",
43
+ userMessage: `请把这个 skill 挂到我现有的 agent 上。skill dbKey 是 ${reference.dbKey}。`,
44
+ },
45
+ {
46
+ id: "create_agent",
47
+ label: "新建一个 Agent 来使用它",
48
+ userMessage: `请基于这个 skill 新建一个 agent 来使用它。skill dbKey 是 ${reference.dbKey}。`,
49
+ },
50
+ ];
51
+
52
+ export const buildSkillFollowupResult = (input: SkillFollowupInput) => {
53
+ const reference = buildSkillReferenceChoice(input.dbKey, input.title);
54
+ const nextActions = buildSkillFollowupChoices(reference);
55
+ const lines = [
56
+ `Skill《${input.title}》已保存为本地文档。`,
57
+ `- dbKey: ${input.dbKey}`,
58
+ input.spaceId ? `- spaceId: ${input.spaceId}` : null,
59
+ input.skillId ? `- skillId: ${input.skillId}` : null,
60
+ input.importedFrom ? `- importedFrom: ${input.importedFrom}` : null,
61
+ Array.isArray(input.toolNames) && input.toolNames.length > 0
62
+ ? `- tools: ${input.toolNames.join(", ")}`
63
+ : null,
64
+ typeof input.hasEvalConfig === "boolean"
65
+ ? `- hasEvalConfig: ${input.hasEvalConfig ? "yes" : "no"}`
66
+ : null,
67
+ "下一步请优先询问用户:仅保存、挂到现有 Agent,还是新建一个 Agent 来使用它。",
68
+ ].filter(Boolean);
69
+
70
+ return {
71
+ rawData: {
72
+ success: true,
73
+ id: input.dbKey,
74
+ dbKey: input.dbKey,
75
+ title: input.title,
76
+ skillId: input.skillId ?? null,
77
+ spaceId: input.spaceId ?? null,
78
+ toolNames: input.toolNames ?? [],
79
+ hasEvalConfig: input.hasEvalConfig ?? false,
80
+ importedFrom: input.importedFrom ?? null,
81
+ reference,
82
+ nextActions,
83
+ },
84
+ displayData: lines.join("\n"),
85
+ };
86
+ };
@@ -0,0 +1,169 @@
1
+ export interface SurfWeatherToolArgs {
2
+ latitude: number;
3
+ longitude: number;
4
+ location_name?: string;
5
+ forecast_days?: number;
6
+ }
7
+
8
+ export const surfWeatherFunctionSchema = {
9
+ name: "surfWeather",
10
+ description: [
11
+ "获取指定海岸位置的冲浪天气预报,包括浪高、涌浪高度、浪周期、涌浪周期、浪向等关键指标。",
12
+ "数据来源:Open-Meteo Marine API(免费,无需 API Key)。",
13
+ "适用场景:查询某个冲浪点未来几天的海浪状况,判断是否适合冲浪。",
14
+ ].join("\n"),
15
+ parameters: {
16
+ type: "object",
17
+ properties: {
18
+ latitude: {
19
+ type: "number",
20
+ description: "纬度,例如海南万宁为 18.8",
21
+ },
22
+ longitude: {
23
+ type: "number",
24
+ description: "经度,例如海南万宁为 110.4",
25
+ },
26
+ location_name: {
27
+ type: "string",
28
+ description: "地点名称(可选),仅用于展示,例如「海南万宁日月湾」",
29
+ },
30
+ forecast_days: {
31
+ type: "number",
32
+ description: "预报天数,1~7,默认 3",
33
+ },
34
+ },
35
+ required: ["latitude", "longitude"],
36
+ },
37
+ } as const;
38
+
39
+ interface HourlyData {
40
+ time: string[];
41
+ wave_height: number[];
42
+ wave_period: number[];
43
+ wave_direction: number[];
44
+ wind_wave_height: number[];
45
+ swell_wave_height: number[];
46
+ swell_wave_period: number[];
47
+ swell_wave_direction: number[];
48
+ }
49
+
50
+ function getSurfRating(waveHeight: number, wavePeriod: number): string {
51
+ if (waveHeight < 0.3) return "⚫ 无浪";
52
+ if (waveHeight < 0.6 && wavePeriod < 6) return "🟤 极差";
53
+ if (waveHeight < 0.8) return "🔴 差";
54
+ if (waveHeight < 1.2 && wavePeriod >= 6) return "🟡 一般";
55
+ if (waveHeight < 2.0 && wavePeriod >= 8) return "🟢 较好";
56
+ if (waveHeight >= 2.0 && wavePeriod >= 10) return "🔵 优秀";
57
+ return "🟡 一般";
58
+ }
59
+
60
+ function getWindDirection(deg: number): string {
61
+ const dirs = ["北", "东北", "东", "东南", "南", "西南", "西", "西北"];
62
+ return dirs[Math.round(deg / 45) % 8];
63
+ }
64
+
65
+ export async function surfWeatherFunc(
66
+ args: SurfWeatherToolArgs
67
+ ): Promise<{ rawData: unknown; displayData: string }> {
68
+ const { latitude, longitude, location_name, forecast_days = 3 } = args;
69
+
70
+ const days = Math.min(Math.max(Math.round(forecast_days), 1), 7);
71
+ const params = [
72
+ "wave_height",
73
+ "wave_period",
74
+ "wave_direction",
75
+ "wind_wave_height",
76
+ "swell_wave_height",
77
+ "swell_wave_period",
78
+ "swell_wave_direction",
79
+ ].join(",");
80
+
81
+ const url =
82
+ `https://marine-api.open-meteo.com/v1/marine` +
83
+ `?latitude=${latitude}&longitude=${longitude}` +
84
+ `&hourly=${params}` +
85
+ `&timezone=Asia/Shanghai` +
86
+ `&forecast_days=${days}`;
87
+
88
+ const res = await fetch(url);
89
+ if (!res.ok) {
90
+ throw new Error(`Open-Meteo API 请求失败: ${res.status} ${res.statusText}`);
91
+ }
92
+
93
+ const data = (await res.json()) as {
94
+ latitude: number;
95
+ longitude: number;
96
+ hourly: HourlyData;
97
+ };
98
+
99
+ const h = data.hourly;
100
+ const locationLabel = location_name || `${data.latitude}°N, ${data.longitude}°E`;
101
+
102
+ // 每天取最大浪高时段作为日摘要(取 08:00 - 20:00 峰值)
103
+ const dailySummaries: {
104
+ date: string;
105
+ maxWaveHeight: number;
106
+ wavePeriod: number;
107
+ swellHeight: number;
108
+ swellPeriod: number;
109
+ waveDirection: number;
110
+ rating: string;
111
+ }[] = [];
112
+
113
+ const totalHours = h.time.length;
114
+ for (let d = 0; d < days; d++) {
115
+ const start = d * 24;
116
+ const end = Math.min(start + 24, totalHours);
117
+ // 找当天最大浪高的小时
118
+ let bestIdx = start;
119
+ for (let i = start; i < end; i++) {
120
+ const hour = parseInt(h.time[i].slice(11, 13), 10);
121
+ if (hour < 8 || hour > 20) continue;
122
+ if (h.wave_height[i] > h.wave_height[bestIdx]) bestIdx = i;
123
+ }
124
+ const date = h.time[bestIdx].slice(0, 10);
125
+ const wh = h.wave_height[bestIdx];
126
+ const wp = h.wave_period[bestIdx];
127
+ const sh = h.swell_wave_height[bestIdx];
128
+ const sp = h.swell_wave_period[bestIdx];
129
+ const wd = h.wave_direction[bestIdx];
130
+ dailySummaries.push({
131
+ date,
132
+ maxWaveHeight: wh,
133
+ wavePeriod: wp,
134
+ swellHeight: sh,
135
+ swellPeriod: sp,
136
+ waveDirection: wd,
137
+ rating: getSurfRating(wh, wp),
138
+ });
139
+ }
140
+
141
+ // 构建展示文本
142
+ const lines: string[] = [];
143
+ lines.push(`🏄 冲浪天气预报 — ${locationLabel}`);
144
+ lines.push(`数据来源:Open-Meteo Marine API\n`);
145
+ lines.push(`日期 评级 浪高 涌浪 周期 涌浪周期 浪向`);
146
+ lines.push("─".repeat(58));
147
+
148
+ for (const s of dailySummaries) {
149
+ lines.push(
150
+ `${s.date} ${s.rating} ${s.maxWaveHeight.toFixed(2)}m ${s.swellHeight.toFixed(2)}m ${s.wavePeriod.toFixed(1)}s ${s.swellPeriod.toFixed(1)}s ${getWindDirection(s.waveDirection)}`
151
+ );
152
+ }
153
+
154
+ lines.push("\n评级说明:🔵优秀 🟢较好 🟡一般 🔴差 🟤极差 ⚫无浪");
155
+
156
+ const displayData = lines.join("\n");
157
+
158
+ return {
159
+ rawData: {
160
+ location: locationLabel,
161
+ latitude: data.latitude,
162
+ longitude: data.longitude,
163
+ forecast_days: days,
164
+ daily: dailySummaries,
165
+ hourly: h,
166
+ },
167
+ displayData,
168
+ };
169
+ }