nolo-cli 0.1.40 → 0.1.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (482) hide show
  1. package/README.md +27 -12
  2. package/agent-runtime/agentThread.ts +222 -0
  3. package/agent-runtime/agentThreadAdmission.ts +74 -0
  4. package/agent-runtime/dialogWritePlan.ts +42 -0
  5. package/agent-runtime/hostAdapter.ts +1 -0
  6. package/agent-runtime/hybridRecordStore.ts +5 -3
  7. package/agent-runtime/index.ts +43 -0
  8. package/agent-runtime/localLoop.ts +31 -2
  9. package/agent-runtime/localToolPolicy.ts +1 -0
  10. package/agent-runtime/localWorkspaceTools.ts +1258 -78
  11. package/agent-runtime/noloWorkspaceTools.ts +20 -1
  12. package/agent-runtime/runtimeToolPolicy.ts +1 -5
  13. package/agent-runtime/runtimeToolSurface.ts +1 -0
  14. package/agentAliases.ts +29 -9
  15. package/agentInternalCommandEntries.ts +11 -3
  16. package/agentMachineCommands.ts +1 -0
  17. package/agentRecordCommands.ts +2 -2
  18. package/agentRecordHelpers.ts +59 -6
  19. package/agentRunCommand.ts +84 -19
  20. package/ai/agent/_executeModel.ts +146 -0
  21. package/ai/agent/agentSlice.ts +556 -1
  22. package/ai/agent/appWorkingMemory.ts +126 -0
  23. package/ai/agent/buildEditingContext.ts +378 -0
  24. package/ai/agent/buildSystemPrompt.ts +599 -0
  25. package/ai/agent/canvasEditingContext.ts +49 -0
  26. package/ai/agent/cleanAgentMessages.ts +140 -0
  27. package/ai/agent/cliChatClient.ts +153 -0
  28. package/ai/agent/cliExecutor.ts +264 -46
  29. package/ai/agent/contextCompiler.ts +107 -0
  30. package/ai/agent/contextLayerContract.ts +44 -0
  31. package/ai/agent/createAgentSchema.ts +272 -0
  32. package/ai/agent/currentTimeContext.ts +65 -0
  33. package/ai/agent/executeToolCall.ts +58 -0
  34. package/ai/agent/fetchAgentContexts.ts +42 -0
  35. package/ai/agent/generatePrompt.ts +3 -0
  36. package/ai/agent/getFullChatContextKeys.ts +168 -0
  37. package/ai/agent/guidedCreation/capabilities.ts +84 -0
  38. package/ai/agent/guidedCreation/types.ts +38 -0
  39. package/ai/agent/isLiveAudioOnlyAgent.ts +26 -0
  40. package/ai/agent/liveAudioModel.ts +2 -0
  41. package/ai/agent/pageBuilderHandoffRules.ts +75 -0
  42. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  43. package/ai/agent/planSlice.ts +259 -0
  44. package/ai/agent/referenceUtils.ts +229 -0
  45. package/ai/agent/runAgentBackground.ts +310 -0
  46. package/ai/agent/runAgentClientLoop.ts +164 -0
  47. package/ai/agent/runtimeGuidance.ts +116 -0
  48. package/ai/agent/serverOrigin.ts +7 -0
  49. package/ai/agent/startParallelAgentStreams.ts +424 -0
  50. package/ai/agent/startupProtocol.ts +53 -0
  51. package/ai/agent/streamAgentChatTurn.ts +2135 -0
  52. package/ai/agent/streamAgentChatTurnUtils.ts +923 -0
  53. package/ai/agent/toolFailureGuard.ts +99 -0
  54. package/ai/agent/types.ts +73 -0
  55. package/ai/agent/utils/imageOutput.ts +37 -0
  56. package/ai/agent/utils/publicImageAgentMode.ts +26 -0
  57. package/ai/ai.locale.ts +1165 -0
  58. package/ai/chat/accumulateToolCallChunks.ts +126 -0
  59. package/ai/chat/fetchUtils.ts +143 -0
  60. package/ai/chat/inlineImageUrlsForCustomProvider.ts +122 -0
  61. package/ai/chat/parseApiError.ts +67 -0
  62. package/ai/chat/parseMultilineSSE.ts +95 -0
  63. package/ai/chat/sendOpenAICompletionsRequest.ts +921 -0
  64. package/ai/chat/sendOpenAIResponseRequest.ts +720 -0
  65. package/ai/chat/serverProxyRetry.ts +102 -0
  66. package/ai/chat/shouldUseServerProxy.ts +18 -0
  67. package/ai/chat/streamRetry.ts +46 -0
  68. package/ai/chat/updateTotalUsage.ts +89 -0
  69. package/ai/context/buildReferenceContext.ts +437 -0
  70. package/ai/context/retention.ts +165 -0
  71. package/ai/context/tokenUtils.ts +78 -0
  72. package/ai/llm/agentCapabilities.ts +74 -0
  73. package/ai/llm/deepinfra.ts +79 -0
  74. package/ai/llm/fireworks.ts +68 -0
  75. package/ai/llm/generateRequestBody.ts +165 -0
  76. package/ai/llm/getModelContextWindow.ts +93 -0
  77. package/ai/llm/getPricing.ts +252 -0
  78. package/ai/llm/imagePricing.ts +66 -0
  79. package/ai/llm/isResponseAPIModel.ts +13 -0
  80. package/ai/llm/kimi.ts +22 -0
  81. package/ai/llm/mimo.ts +99 -0
  82. package/ai/llm/mistral.ts +21 -0
  83. package/ai/llm/models.ts +45 -0
  84. package/ai/llm/openrouterModels.ts +31 -0
  85. package/ai/llm/providers.ts +313 -0
  86. package/ai/llm/reasoningModels.ts +27 -0
  87. package/ai/llm/types.ts +82 -0
  88. package/ai/llm/usageRequestOptions.ts +59 -0
  89. package/ai/memory/policy.ts +49 -0
  90. package/ai/memory/queryShared.ts +167 -0
  91. package/ai/memory/recentRelationshipRecap.ts +251 -0
  92. package/ai/memory/remember.ts +111 -0
  93. package/ai/memory/scope.ts +74 -0
  94. package/ai/memory/store.ts +20 -0
  95. package/ai/memory/storeShared.ts +76 -0
  96. package/ai/memory/types.ts +52 -0
  97. package/ai/memory/understanding.ts +341 -0
  98. package/ai/memory/understandingGreeting.ts +264 -0
  99. package/ai/policy/personalizationDialog.ts +333 -0
  100. package/ai/policy/runtimePolicy.ts +526 -0
  101. package/ai/policy/selfUpdateFields.ts +48 -0
  102. package/ai/policy/types.ts +64 -0
  103. package/ai/skills/referenceRuntime.ts +274 -0
  104. package/ai/skills/skillDiagnostics.ts +251 -0
  105. package/ai/skills/skillDocBuilder.ts +139 -0
  106. package/ai/skills/skillSummaryMarker.ts +26 -0
  107. package/ai/token/calculatePrice.ts +576 -0
  108. package/ai/token/missingUsageEstimate.ts +42 -0
  109. package/ai/token/normalizeUsage.ts +124 -0
  110. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  111. package/ai/token/prepareTokenUsageData.ts +93 -0
  112. package/ai/token/resolveBillingTarget.ts +52 -0
  113. package/ai/token/saveTokenRecord.ts +53 -0
  114. package/ai/token/types.ts +103 -0
  115. package/ai/tools/agent/agentTools.ts +214 -0
  116. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  117. package/ai/tools/agent/callAgentTool.ts +139 -0
  118. package/ai/tools/agent/createAgentTool.ts +512 -0
  119. package/ai/tools/agent/createDialogTool.ts +69 -0
  120. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  121. package/ai/tools/agent/parallelBudget.ts +221 -0
  122. package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
  123. package/ai/tools/agent/presets/appBuilderPreset.ts +154 -0
  124. package/ai/tools/agent/runLlmTool.ts +96 -0
  125. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  126. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  127. package/ai/tools/agent/startAgentDialogTool.ts +54 -0
  128. package/ai/tools/agent/streamParallelAgentsTool.ts +123 -0
  129. package/ai/tools/agent/updateAgentTool.ts +96 -0
  130. package/ai/tools/agent/updateSelfTool.ts +113 -0
  131. package/ai/tools/amazonProductScraperTool.ts +86 -0
  132. package/ai/tools/apifyActorClient.ts +45 -0
  133. package/ai/tools/appEditGuard.ts +372 -0
  134. package/ai/tools/appReadSnapshot.ts +153 -0
  135. package/ai/tools/appTools.ts +1549 -0
  136. package/ai/tools/applyEditTool.ts +259 -0
  137. package/ai/tools/applyLineEditsTool.ts +315 -0
  138. package/ai/tools/browserTools/click.ts +33 -0
  139. package/ai/tools/browserTools/closeSession.ts +29 -0
  140. package/ai/tools/browserTools/common.ts +27 -0
  141. package/ai/tools/browserTools/openSession.ts +48 -0
  142. package/ai/tools/browserTools/readContent.ts +38 -0
  143. package/ai/tools/browserTools/selectOption.ts +46 -0
  144. package/ai/tools/browserTools/typeText.ts +42 -0
  145. package/ai/tools/category/createCategoryTool.ts +66 -0
  146. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  147. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  148. package/ai/tools/cfBrowserTools.ts +319 -0
  149. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  150. package/ai/tools/checkEnvTool.ts +102 -0
  151. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  152. package/ai/tools/codeSearchTool.ts +123 -0
  153. package/ai/tools/codeTools.ts +101 -0
  154. package/ai/tools/convertMarxistsBookTool.ts +103 -0
  155. package/ai/tools/createDocTool.ts +132 -0
  156. package/ai/tools/createSkillDocTool.ts +155 -0
  157. package/ai/tools/createWorkflowTool.ts +154 -0
  158. package/ai/tools/delayTool.ts +31 -0
  159. package/ai/tools/deleteSpacesTool.ts +325 -0
  160. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  161. package/ai/tools/devReloadUtils.ts +29 -0
  162. package/ai/tools/dialogMessageSearch.ts +137 -0
  163. package/ai/tools/doctorSkillTool.ts +72 -0
  164. package/ai/tools/ecommerceScraperTool.ts +86 -0
  165. package/ai/tools/emailTools.ts +549 -0
  166. package/ai/tools/evalSkillTool.ts +92 -0
  167. package/ai/tools/exaSearchTool.ts +63 -0
  168. package/ai/tools/execShellTool.ts +266 -0
  169. package/ai/tools/executeSqlTool.ts +192 -0
  170. package/ai/tools/fetchWebpageSupport.ts +333 -0
  171. package/ai/tools/fetchWebpageTool.ts +97 -0
  172. package/ai/tools/geminiImagePreviewTool.ts +386 -0
  173. package/ai/tools/generateDocxTool.ts +215 -0
  174. package/ai/tools/googleSearchScraperTool.ts +106 -0
  175. package/ai/tools/importDataTool.ts +133 -0
  176. package/ai/tools/importSkillTool.ts +162 -0
  177. package/ai/tools/index.ts +2290 -0
  178. package/ai/tools/jdProductScraperTool.ts +821 -0
  179. package/ai/tools/listUserSpacesTool.ts +113 -0
  180. package/ai/tools/localFilesTool.ts +276 -0
  181. package/ai/tools/marxistsOfflineBook.ts +253 -0
  182. package/ai/tools/modelUsageTools.ts +206 -0
  183. package/ai/tools/noloWorkspaceReadTools.ts +467 -0
  184. package/ai/tools/olmOcrTool.ts +33 -0
  185. package/ai/tools/openaiImageTool.ts +267 -0
  186. package/ai/tools/prepareTools.ts +193 -0
  187. package/ai/tools/readDocTool.ts +88 -0
  188. package/ai/tools/readFileTool.ts +214 -0
  189. package/ai/tools/readTool.ts +194 -0
  190. package/ai/tools/readXPostTool.ts +229 -0
  191. package/ai/tools/readXhsProfileTool.ts +638 -0
  192. package/ai/tools/rememberMemoryTool.ts +84 -0
  193. package/ai/tools/remotionVideoTool.ts +151 -0
  194. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  195. package/ai/tools/searchWorkspaceTool.ts +259 -0
  196. package/ai/tools/skillFollowup.ts +86 -0
  197. package/ai/tools/surfWeatherTool.ts +169 -0
  198. package/ai/tools/table/addTableRowTool.ts +217 -0
  199. package/ai/tools/table/createTableTool.ts +326 -0
  200. package/ai/tools/table/rowTools.ts +386 -0
  201. package/ai/tools/table/schemaTools.ts +244 -0
  202. package/ai/tools/table/shareTableTool.ts +148 -0
  203. package/ai/tools/table/toolShared.ts +129 -0
  204. package/ai/tools/taobaoTmallProductScraperTool.ts +49 -0
  205. package/ai/tools/toolApiClient.ts +279 -0
  206. package/ai/tools/toolPacks.ts +19 -0
  207. package/ai/tools/toolResultError.ts +42 -0
  208. package/ai/tools/toolRunSlice.ts +303 -0
  209. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  210. package/ai/tools/uiAskChoiceTool.ts +104 -0
  211. package/ai/tools/updateContentTitleTool.ts +84 -0
  212. package/ai/tools/updateDocTool.ts +105 -0
  213. package/ai/tools/updateUserPreferenceProfileTool.ts +153 -0
  214. package/ai/tools/wereadGatewayTool.ts +152 -0
  215. package/ai/tools/whisperTool.ts +76 -0
  216. package/ai/tools/writeFileTool.ts +213 -0
  217. package/ai/tools/youtubeScraperTool.ts +116 -0
  218. package/ai/tools/ziweiChartTool.ts +678 -0
  219. package/ai/types.ts +58 -0
  220. package/ai/workflow/workflowExecutor.ts +323 -0
  221. package/ai/workflow/workflowSlice.ts +73 -0
  222. package/ai/workflow/workflowTypes.ts +106 -0
  223. package/app/actions/syncAppRecord.ts +73 -0
  224. package/app/constants/animationSets.ts +21 -0
  225. package/app/constants/appEditor.ts +99 -0
  226. package/app/favorite/deletedFavoriteProjection.ts +31 -0
  227. package/app/favorite/favoriteSlice.ts +690 -0
  228. package/app/favorite/type.ts +43 -0
  229. package/app/hooks/deleteDbKey.ts +254 -0
  230. package/app/i18n/client.ts +13 -0
  231. package/app/i18n/i18n.base.ts +14 -0
  232. package/app/i18n/i18n.config.ts +77 -0
  233. package/app/i18n/index.ts +15 -0
  234. package/app/i18n/mapLanguage.ts +11 -0
  235. package/app/i18n/translations/appBuilder.locale.ts +453 -0
  236. package/app/i18n/translations/error.locale.ts +246 -0
  237. package/app/i18n/translations/interface.locale.ts +2206 -0
  238. package/app/i18n/translations/pricing.locale.ts +397 -0
  239. package/app/i18n/translations/privacy.locale.ts +168 -0
  240. package/app/i18n/translations/seo.locale.ts +129 -0
  241. package/app/i18n/translations/terms.locale.ts +124 -0
  242. package/app/i18n/types.ts +6 -0
  243. package/app/notifications/model.ts +77 -0
  244. package/app/notifications/notificationSlice.ts +141 -0
  245. package/app/reducer.ts +36 -0
  246. package/app/settings/dbActionThunks.ts +34 -0
  247. package/app/settings/defaultSpacePreference.ts +87 -0
  248. package/app/settings/settingSlice.tsx +1212 -0
  249. package/app/settings/settingsRecord.ts +8 -0
  250. package/app/stateViews/runtime.ts +90 -0
  251. package/app/store.ts +55 -0
  252. package/app/theme/colors.ts +280 -0
  253. package/app/theme/fontPreference.ts +77 -0
  254. package/app/theme/theme.config.ts +120 -0
  255. package/app/theme/themeModeBootstrap.ts +123 -0
  256. package/app/types/appSummary.ts +118 -0
  257. package/app/types.ts +358 -0
  258. package/app/utils/appKeys.ts +55 -0
  259. package/app/utils/async.ts +28 -0
  260. package/app/utils/desktopAgentRuntimeTurnClient.ts +75 -0
  261. package/app/utils/env.ts +17 -0
  262. package/app/utils/fileUtils.ts +229 -0
  263. package/app/utils/imageUtils.ts +274 -0
  264. package/app/utils/myContentItems.ts +289 -0
  265. package/app/utils/retryAfter.ts +36 -0
  266. package/app/utils/retryFetch.ts +61 -0
  267. package/app/utils/toast.ts +3 -0
  268. package/auth/action/signUpAction.ts +227 -0
  269. package/auth/adminPermissions.ts +78 -0
  270. package/auth/authSlice.ts +531 -0
  271. package/auth/client/loginRequest.ts +14 -0
  272. package/auth/resetAuthScopedClientState.ts +27 -0
  273. package/auth/routes.ts +183 -0
  274. package/auth/token.ts +63 -0
  275. package/auth/types.ts +40 -0
  276. package/chat/chat.locale.ts +817 -0
  277. package/chat/dialog/actions/addCybotAction.ts +52 -0
  278. package/chat/dialog/actions/addReferenceKeysAction.ts +62 -0
  279. package/chat/dialog/actions/builtinDialogLlm.ts +35 -0
  280. package/chat/dialog/actions/cleanupCliSession.ts +40 -0
  281. package/chat/dialog/actions/createDialogAction.ts +379 -0
  282. package/chat/dialog/actions/createScheduledTaskAction.ts +78 -0
  283. package/chat/dialog/actions/handleSendMessageAction.ts +202 -0
  284. package/chat/dialog/actions/handleSendMessageResolver.ts +27 -0
  285. package/chat/dialog/actions/removeCybotAction.ts +6 -0
  286. package/chat/dialog/actions/removeDialogAgentAction.ts +44 -0
  287. package/chat/dialog/actions/replacePrimaryCybotAction.ts +6 -0
  288. package/chat/dialog/actions/setPrimaryDialogAgentAction.ts +52 -0
  289. package/chat/dialog/actions/updateDialogSummaryAction.ts +430 -0
  290. package/chat/dialog/actions/updateDialogTitleAction.ts +151 -0
  291. package/chat/dialog/actions/updateDialogTitlePolicy.ts +30 -0
  292. package/chat/dialog/actions/updateTokensAction.ts +233 -0
  293. package/chat/dialog/dialogAgents.ts +72 -0
  294. package/chat/dialog/dialogAttachmentCleanup.ts +135 -0
  295. package/chat/dialog/dialogSlice.ts +985 -0
  296. package/chat/dialog/dialogTitle.ts +81 -0
  297. package/chat/dialog/dialogTokenStats.ts +12 -0
  298. package/chat/dialog/dialogUrl.ts +6 -0
  299. package/chat/dialog/goal.ts +108 -0
  300. package/chat/dialog/objectAssistantRegistry.ts +286 -0
  301. package/chat/dialog/schedule.ts +17 -0
  302. package/chat/messages/activityCompletion.ts +97 -0
  303. package/chat/messages/fetchAndCacheMessages.ts +119 -0
  304. package/chat/messages/fetchConvMsgs.ts +51 -0
  305. package/chat/messages/fileUrl.ts +13 -0
  306. package/chat/messages/imagePayloadPersistence.ts +22 -0
  307. package/chat/messages/messageAgent.ts +8 -0
  308. package/chat/messages/messageContent.ts +203 -0
  309. package/chat/messages/messageContract.ts +88 -0
  310. package/chat/messages/messageSlice.ts +1386 -0
  311. package/chat/messages/toolThunks.ts +646 -0
  312. package/chat/messages/types.ts +8 -1
  313. package/chat/messages/web/toolDisplayName.ts +686 -0
  314. package/cli/agentAliases.ts +29 -9
  315. package/cli/agentInternalCommandEntries.ts +11 -3
  316. package/cli/agentMachineCommands.ts +1 -0
  317. package/cli/agentRecordCommands.ts +2 -2
  318. package/cli/agentRecordHelpers.ts +59 -6
  319. package/cli/agentRunCommand.ts +84 -19
  320. package/cli/client/agentRun.ts +92 -261
  321. package/cli/client/localRuntimeAdapter.ts +292 -50
  322. package/cli/commandRegistry.ts +7 -4
  323. package/cli/dialogAttachmentCleanup.ts +151 -0
  324. package/cli/dialogCommands.ts +163 -61
  325. package/cli/dialogInternalCommandEntries.ts +1 -1
  326. package/cli/docCreateCommands.ts +345 -0
  327. package/cli/docInternalCommandEntries.ts +11 -0
  328. package/cli/docPageHelpers.ts +80 -0
  329. package/cli/docSkillHelpers.ts +67 -0
  330. package/cli/docSpaceHelpers.ts +46 -0
  331. package/cli/internalCommandEntries.ts +4 -0
  332. package/cli/machineCommands.ts +18 -34
  333. package/cli/memoryCommands.ts +253 -0
  334. package/cli/memoryInternalCommandEntries.ts +9 -0
  335. package/cli/qoderUsageProbe.ts +252 -0
  336. package/cli/scriptCommandEntries.ts +0 -3
  337. package/cli/secretScan.ts +31 -0
  338. package/cli/tableCommands.ts +248 -13
  339. package/cli/workflowInternalCommandEntries.ts +2 -3
  340. package/client/agentRun.test.ts +119 -83
  341. package/client/agentRun.ts +92 -261
  342. package/client/localRuntimeAdapter.test.ts +894 -32
  343. package/client/localRuntimeAdapter.ts +292 -50
  344. package/commandRegistry.ts +7 -4
  345. package/connector-experimental/capabilities.ts +10 -9
  346. package/core/builtinAgents.ts +209 -0
  347. package/core/config.ts +15 -0
  348. package/core/crypto.ts +51 -0
  349. package/core/generateKeyPairFromSeedV1.ts +15 -0
  350. package/core/generateMainKey.ts +22 -0
  351. package/core/init.ts +41 -0
  352. package/core/localOrigins.ts +8 -0
  353. package/core/password.ts +13 -0
  354. package/core/prefix.ts +23 -0
  355. package/create/editor/transforms/fromMarkdown/index.ts +260 -0
  356. package/create/editor/transforms/fromMarkdown/inline.ts +285 -0
  357. package/create/editor/transforms/fromMarkdown/table.ts +130 -0
  358. package/create/editor/transforms/markdownToSlate.ts +24 -0
  359. package/create/editor/transforms/slateToRenderMarkdown.ts +218 -0
  360. package/create/editor/transforms/slateToSimplifiedMarkdown.ts +224 -0
  361. package/create/editor/transforms/slateToText.ts +33 -0
  362. package/create/editor/utils/slateUtils.ts +233 -0
  363. package/create/space/addSpaceAction.ts +193 -0
  364. package/create/space/category/categoryActions.ts +427 -0
  365. package/create/space/changeSpaceAction.ts +36 -0
  366. package/create/space/constants.ts +8 -0
  367. package/create/space/content/addContentAction.ts +116 -0
  368. package/create/space/content/contentThunks.ts +90 -0
  369. package/create/space/content/deleteContentFromSpaceAction.ts +187 -0
  370. package/create/space/content/deleteMultipleContentAction.ts +112 -0
  371. package/create/space/content/moveContentAction.ts +285 -0
  372. package/create/space/content/updateContentCategoryAction.ts +154 -0
  373. package/create/space/content/updateContentTitleAction.ts +151 -0
  374. package/create/space/content/uploadAndAddFileToSpaceAction.ts +98 -0
  375. package/create/space/contentKeyUtils.ts +117 -0
  376. package/create/space/deleteSpaceAction.ts +276 -0
  377. package/create/space/fetchSpaceAction.ts +70 -0
  378. package/create/space/fetchSpaceSidebarStateAction.ts +26 -0
  379. package/create/space/loadDefaultSpaceAction.ts +57 -0
  380. package/create/space/member/addMemberAction.ts +79 -0
  381. package/create/space/member/fetchUserSpaceMembershipsAction.ts +386 -0
  382. package/create/space/member/memberThunks.ts +47 -0
  383. package/create/space/member/removeMemberAction.ts +67 -0
  384. package/create/space/resolvePreferredSpaceId.ts +113 -0
  385. package/create/space/sidebarVisibleTypes.ts +260 -0
  386. package/create/space/space.locale.ts +1087 -0
  387. package/create/space/spaceAccess.ts +182 -0
  388. package/create/space/spaceCollapsedState.ts +54 -0
  389. package/create/space/spaceKeys.ts +65 -0
  390. package/create/space/spaceSlice.ts +328 -0
  391. package/create/space/spaceThunks.ts +410 -0
  392. package/create/space/types.ts +27 -0
  393. package/create/space/updateSpaceAction.ts +107 -0
  394. package/create/space/utils/permissions.ts +47 -0
  395. package/create/types.ts +21 -0
  396. package/database/actions/agentReadResolution.ts +33 -0
  397. package/database/actions/common.ts +243 -0
  398. package/database/actions/deleteFile.ts +40 -0
  399. package/database/actions/fileContent.ts +125 -0
  400. package/database/actions/patch.ts +155 -0
  401. package/database/actions/read.ts +358 -0
  402. package/database/actions/readAndWait.ts +260 -0
  403. package/database/actions/readRequestManager.ts +120 -0
  404. package/database/actions/readResolution.ts +125 -0
  405. package/database/actions/remove.ts +94 -0
  406. package/database/actions/replication.ts +477 -0
  407. package/database/actions/upload.ts +186 -0
  408. package/database/actions/upsert.ts +56 -0
  409. package/database/actions/write.ts +146 -0
  410. package/database/authority/ownerKey.ts +80 -0
  411. package/database/authority/recordAuthority.ts +67 -0
  412. package/database/authority/userAuthorityRegistry.ts +54 -0
  413. package/database/client/fetchUserData.ts +61 -0
  414. package/database/dbActionThunks.ts +1 -0
  415. package/database/dbSlice.ts +149 -0
  416. package/database/fileRing.ts +51 -0
  417. package/database/fileStorage.ts +232 -0
  418. package/database/fileUrl.ts +34 -0
  419. package/database/keys.ts +767 -0
  420. package/database/queryPrefixes.ts +14 -0
  421. package/database/requests.ts +443 -0
  422. package/database/runtimeServerContext.ts +54 -0
  423. package/database/tenantPlacement.ts +57 -0
  424. package/database/userPreferenceRegister.ts +131 -0
  425. package/database/utils/ulid.ts +1 -0
  426. package/dialogAttachmentCleanup.ts +151 -0
  427. package/dialogCommands.ts +163 -61
  428. package/dialogInternalCommandEntries.ts +1 -1
  429. package/docCreateCommands.ts +345 -0
  430. package/docInternalCommandEntries.ts +11 -0
  431. package/docPageHelpers.ts +80 -0
  432. package/docSkillHelpers.ts +67 -0
  433. package/docSpaceHelpers.ts +46 -0
  434. package/integrations/anthropic/anthropicModels.ts +47 -0
  435. package/integrations/deepseek/models.ts +37 -0
  436. package/integrations/google/models.ts +341 -0
  437. package/integrations/openai/chatCompletionStreamMode.ts +10 -0
  438. package/integrations/openai/filterAndCleanMessages.ts +284 -0
  439. package/integrations/openai/flexTier.ts +102 -0
  440. package/integrations/openai/generateOpenAIRequestBody.ts +271 -0
  441. package/integrations/openai/generateResponseRequestBody.ts +77 -0
  442. package/integrations/openai/models.ts +271 -0
  443. package/integrations/openai/providerBodyCompatibility.ts +23 -0
  444. package/integrations/openai/responsesHelpers.ts +297 -0
  445. package/integrations/x-reader/types.ts +109 -0
  446. package/integrations/xai/models.ts +13 -0
  447. package/integrations/xhs-reader/redaction.ts +68 -0
  448. package/integrations/xhs-reader/types.ts +277 -0
  449. package/integrations/xhs-reader/url.ts +80 -0
  450. package/internalCommandEntries.ts +4 -0
  451. package/machineCommands.ts +18 -34
  452. package/memoryCommands.ts +253 -0
  453. package/memoryInternalCommandEntries.ts +9 -0
  454. package/package.json +454 -9
  455. package/qoderUsageProbe.ts +252 -0
  456. package/render/contentIcon/types.ts +167 -0
  457. package/render/page/createPageAction.ts +183 -0
  458. package/render/page/docSlice.ts +495 -0
  459. package/render/page/types.ts +19 -0
  460. package/render/table/createTableAction.ts +274 -0
  461. package/render/table/deleteTableAction.ts +193 -0
  462. package/render/table/fetchAndCacheTableRows.ts +174 -0
  463. package/render/table/tableSlice.ts +1167 -0
  464. package/render/table/toolValueUtils.ts +363 -0
  465. package/render/table/types.ts +262 -0
  466. package/render/table/utils/tableSerialization.ts +50 -0
  467. package/scriptCommandEntries.ts +0 -3
  468. package/secretScan.ts +31 -0
  469. package/server/handlers/agentRun/types.ts +105 -0
  470. package/share/action.ts +225 -0
  471. package/share/helpers.ts +302 -0
  472. package/share/keys.ts +243 -0
  473. package/share/publicSharePolicy.ts +27 -0
  474. package/share/shareSlice.ts +53 -0
  475. package/share/types.ts +100 -0
  476. package/tableCommands.ts +248 -13
  477. package/workflowInternalCommandEntries.ts +2 -3
  478. package/ai/tools/agent/taskRunTool.ts +0 -112
  479. package/cli/client/taskRunPrompt.ts +0 -27
  480. package/cli/taskRunCommand.ts +0 -265
  481. package/client/taskRunPrompt.ts +0 -27
  482. package/taskRunCommand.ts +0 -265
@@ -13,14 +13,63 @@ type LocalWorkspaceToolArgs = {
13
13
  commandPrefix?: string[];
14
14
  };
15
15
 
16
+ export type ActivityRef =
17
+ | { type: "file"; path: string }
18
+ | { type: "terminal"; id?: string; label?: string }
19
+ | { type: "url"; url: string; label?: string };
20
+
21
+ export type ToolActivityAction = {
22
+ title: string;
23
+ kind?: string;
24
+ detail?: string;
25
+ refs?: ActivityRef[];
26
+ };
27
+
28
+ export type ToolActivityPhase = {
29
+ id: string;
30
+ title: string;
31
+ index?: number;
32
+ total?: number;
33
+ status?: "pending" | "running" | "success" | "failed";
34
+ };
35
+
36
+ export type ActivityPlan = {
37
+ title?: string;
38
+ phases: Array<{
39
+ id: string;
40
+ title: string;
41
+ index?: number;
42
+ status?: "pending" | "running" | "success" | "failed";
43
+ }>;
44
+ };
45
+
46
+ export type ToolActivity = Partial<ToolActivityAction> & {
47
+ phase?: ToolActivityPhase;
48
+ action?: ToolActivityAction;
49
+ plan?: ActivityPlan;
50
+ };
51
+
16
52
  type WorkspaceFileArgs = {
17
53
  path?: unknown;
18
54
  content?: unknown;
19
55
  oldText?: unknown;
20
56
  newText?: unknown;
21
57
  expectedReplacements?: unknown;
58
+ maxDepth?: unknown;
59
+ entryType?: unknown;
60
+ startLine?: unknown;
61
+ endLine?: unknown;
62
+ maxLines?: unknown;
63
+ tailLines?: unknown;
22
64
  query?: unknown;
65
+ pattern?: unknown;
66
+ glob?: unknown;
67
+ exclude?: unknown;
68
+ maxResults?: unknown;
23
69
  includeIgnored?: unknown;
70
+ literal?: unknown;
71
+ caseSensitive?: unknown;
72
+ contextLines?: unknown;
24
73
  command?: unknown;
25
74
  cmd?: unknown;
26
75
  branch?: unknown;
@@ -36,17 +85,28 @@ type WorkspaceFileArgs = {
36
85
  screenshotPath?: unknown;
37
86
  metricsPath?: unknown;
38
87
  shell?: unknown;
88
+ _activity?: unknown;
39
89
  };
40
90
 
41
91
  type OpenAiCompatibleTool = Record<string, unknown> & {
42
92
  function?: Record<string, unknown> & { name?: string };
43
93
  };
44
94
 
95
+ export type GlobFilesDescriptionVariant = "brief" | "strategy" | "workflow" | "antiShell";
96
+ export type GlobFilesParameterVariant = "minimal" | "scoped" | "rich";
97
+ export type SearchFilesDescriptionVariant = "brief" | "strategy" | "workflow" | "antiShell";
98
+ export type SearchFilesParameterVariant = "minimal" | "scoped" | "rich";
99
+ export type ReadFileDescriptionVariant = "brief" | "strategy" | "workflow" | "antiShell";
100
+ export type ReadFileParameterVariant = "minimal" | "scoped" | "rich";
101
+ export type ListFilesDescriptionVariant = "brief" | "strategy" | "workflow" | "antiShell";
102
+ export type ListFilesParameterVariant = "minimal" | "scoped" | "rich";
103
+
45
104
  const WORKSPACE_TOOL_NAMES = [
46
105
  "listFiles",
47
106
  "readFile",
48
107
  "writeFile",
49
108
  "editFile",
109
+ "globFiles",
50
110
  "searchFiles",
51
111
  "startPreview",
52
112
  "getPreviewStatus",
@@ -61,6 +121,7 @@ const DEFAULT_LOCAL_CODING_TOOL_NAMES = [
61
121
  "readFile",
62
122
  "writeFile",
63
123
  "editFile",
124
+ "globFiles",
64
125
  "searchFiles",
65
126
  ] as const;
66
127
 
@@ -75,6 +136,199 @@ const REMOVED_WORKSPACE_TOOL_NAMES = new Set([
75
136
  "commitWorkspace",
76
137
  ]);
77
138
 
139
+ function buildActivityProperty() {
140
+ const refsProperty = {
141
+ type: "array",
142
+ items: {
143
+ type: "object",
144
+ properties: {
145
+ type: { type: "string", enum: ["file", "terminal", "url"] },
146
+ path: { type: "string" },
147
+ id: { type: "string" },
148
+ label: { type: "string" },
149
+ url: { type: "string" },
150
+ },
151
+ },
152
+ description: "Referenced files, terminals, or URLs for this action.",
153
+ };
154
+ const actionProperty = {
155
+ type: "object",
156
+ properties: {
157
+ title: { type: "string", description: "Short human-readable action label." },
158
+ kind: {
159
+ type: "string",
160
+ enum: ["read", "write", "edit", "search", "terminal", "version", "test", "build", "preview", "other"],
161
+ },
162
+ detail: { type: "string", description: "Optional concise detail shown after the label." },
163
+ refs: refsProperty,
164
+ },
165
+ required: ["title"],
166
+ };
167
+ const planPhaseProperty = {
168
+ type: "object",
169
+ properties: {
170
+ id: { type: "string", description: "Stable phase id reused by activity.phase.id." },
171
+ title: { type: "string", description: "Human-readable planned task phase." },
172
+ index: { type: "number" },
173
+ status: { type: "string", enum: ["pending", "running", "success", "failed"] },
174
+ },
175
+ required: ["id", "title"],
176
+ };
177
+ return {
178
+ type: "object",
179
+ description:
180
+ "Optional lightweight human-readable activity hint for UI display only. Prefer { plan, phase, action }. The executor ignores this for execution semantics but echoes it back in result metadata.",
181
+ properties: {
182
+ title: { type: "string", description: "Short human-readable action label." },
183
+ kind: {
184
+ type: "string",
185
+ enum: ["read", "write", "edit", "search", "terminal", "version", "test", "build", "preview", "other"],
186
+ },
187
+ detail: { type: "string", description: "Optional longer description shown on expand." },
188
+ refs: refsProperty,
189
+ plan: {
190
+ type: "object",
191
+ properties: {
192
+ title: { type: "string", description: "Optional label for the visible task progress block." },
193
+ phases: {
194
+ type: "array",
195
+ items: planPhaseProperty,
196
+ },
197
+ },
198
+ required: ["phases"],
199
+ },
200
+ phase: {
201
+ type: "object",
202
+ properties: {
203
+ id: { type: "string", description: "Stable phase id reused across related tool calls." },
204
+ title: { type: "string", description: "Human-readable current task phase." },
205
+ index: { type: "number" },
206
+ total: { type: "number" },
207
+ status: { type: "string", enum: ["pending", "running", "success", "failed"] },
208
+ },
209
+ required: ["id", "title"],
210
+ },
211
+ action: actionProperty,
212
+ },
213
+ };
214
+ }
215
+
216
+ function readTrimmedString(value: unknown): string | undefined {
217
+ if (typeof value !== "string") return undefined;
218
+ const trimmed = value.trim();
219
+ return trimmed || undefined;
220
+ }
221
+
222
+ function readFiniteNumber(value: unknown): number | undefined {
223
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
224
+ }
225
+
226
+ function extractActivityRefs(rawRefs: unknown): ActivityRef[] | undefined {
227
+ if (!Array.isArray(rawRefs)) return undefined;
228
+ const refs = rawRefs.flatMap((entry): ActivityRef[] => {
229
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
230
+ const ref = entry as Record<string, unknown>;
231
+ if (ref.type === "file") {
232
+ const path = readTrimmedString(ref.path);
233
+ return path ? [{ type: "file", path }] : [];
234
+ }
235
+ if (ref.type === "terminal") {
236
+ const id = readTrimmedString(ref.id);
237
+ const label = readTrimmedString(ref.label);
238
+ return id || label
239
+ ? [{ type: "terminal", ...(id ? { id } : {}), ...(label ? { label } : {}) }]
240
+ : [];
241
+ }
242
+ if (ref.type === "url") {
243
+ const url = readTrimmedString(ref.url);
244
+ const label = readTrimmedString(ref.label);
245
+ return url ? [{ type: "url", url, ...(label ? { label } : {}) }] : [];
246
+ }
247
+ return [];
248
+ });
249
+ return refs.length ? refs : undefined;
250
+ }
251
+
252
+ function extractActivityAction(rawAction: unknown): ToolActivityAction | undefined {
253
+ if (!rawAction || typeof rawAction !== "object" || Array.isArray(rawAction)) return undefined;
254
+ const obj = rawAction as Record<string, unknown>;
255
+ const title = readTrimmedString(obj.title);
256
+ if (!title) return undefined;
257
+ const kind = readTrimmedString(obj.kind);
258
+ const detail = readTrimmedString(obj.detail);
259
+ const refs = extractActivityRefs(obj.refs);
260
+ return {
261
+ title,
262
+ ...(kind ? { kind } : {}),
263
+ ...(detail ? { detail } : {}),
264
+ ...(refs ? { refs } : {}),
265
+ };
266
+ }
267
+
268
+ function extractActivityPhase(rawPhase: unknown): ToolActivityPhase | undefined {
269
+ if (!rawPhase || typeof rawPhase !== "object" || Array.isArray(rawPhase)) return undefined;
270
+ const obj = rawPhase as Record<string, unknown>;
271
+ const title = readTrimmedString(obj.title);
272
+ if (!title) return undefined;
273
+ const id = readTrimmedString(obj.id) || title.toLowerCase().replace(/\s+/g, "-");
274
+ const index = readFiniteNumber(obj.index);
275
+ const total = readFiniteNumber(obj.total);
276
+ const status =
277
+ obj.status === "pending" ||
278
+ obj.status === "running" ||
279
+ obj.status === "success" ||
280
+ obj.status === "failed"
281
+ ? obj.status
282
+ : undefined;
283
+ return {
284
+ id,
285
+ title,
286
+ ...(index !== undefined ? { index } : {}),
287
+ ...(total !== undefined ? { total } : {}),
288
+ ...(status ? { status } : {}),
289
+ };
290
+ }
291
+
292
+ function extractActivityPlan(rawPlan: unknown): ActivityPlan | undefined {
293
+ if (!rawPlan || typeof rawPlan !== "object" || Array.isArray(rawPlan)) return undefined;
294
+ const obj = rawPlan as Record<string, unknown>;
295
+ if (!Array.isArray(obj.phases)) return undefined;
296
+ const phases = obj.phases.flatMap((entry, index): ActivityPlan["phases"] => {
297
+ const phase = extractActivityPhase(entry);
298
+ if (!phase) return [];
299
+ return [{
300
+ id: phase.id,
301
+ title: phase.title,
302
+ index: phase.index ?? index + 1,
303
+ ...(phase.status ? { status: phase.status } : {}),
304
+ }];
305
+ });
306
+ if (phases.length === 0) return undefined;
307
+ const title = readTrimmedString(obj.title);
308
+ return {
309
+ ...(title ? { title } : {}),
310
+ phases,
311
+ };
312
+ }
313
+
314
+ function extractActivity(parsed: WorkspaceFileArgs): ToolActivity | undefined {
315
+ const raw = parsed._activity;
316
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
317
+ const obj = raw as Record<string, unknown>;
318
+ const nestedAction = extractActivityAction(obj.action);
319
+ const legacyAction = extractActivityAction(obj);
320
+ const action = nestedAction || legacyAction;
321
+ const phase = extractActivityPhase(obj.phase);
322
+ const plan = extractActivityPlan(obj.plan);
323
+ if (!action && !plan) return undefined;
324
+ return {
325
+ ...(action ? action : {}),
326
+ ...(phase ? { phase } : {}),
327
+ ...(nestedAction ? { action: nestedAction } : {}),
328
+ ...(plan ? { plan } : {}),
329
+ };
330
+ }
331
+
78
332
  function buildWorkspacePathProperty() {
79
333
  return {
80
334
  type: "string",
@@ -82,36 +336,153 @@ function buildWorkspacePathProperty() {
82
336
  };
83
337
  }
84
338
 
85
- function buildListWorkspaceFilesTool(): OpenAiCompatibleTool {
339
+ function buildListWorkspaceDescription(variant?: ListFilesDescriptionVariant) {
340
+ if (variant === "brief") {
341
+ return "List files and directories inside a workspace directory.";
342
+ }
343
+ if (variant === "workflow") {
344
+ return "List a bounded directory overview inside the workspace. Use first when you need to understand nearby structure, choose a subdirectory, or inspect a non-code project. Then use globFiles for path-pattern discovery, searchFiles for text/content discovery, and readFile only for the specific files or line ranges you need.";
345
+ }
346
+ if (variant === "antiShell") {
347
+ return "List a bounded directory overview inside the workspace. Do not use execShell with ls/find/tree for normal directory overviews when listFiles can show the needed depth and limit. Use globFiles for path-pattern discovery and searchFiles for content search.";
348
+ }
349
+ return "List a bounded directory overview inside the workspace. Use for scanning nearby structure, not for path-pattern discovery across the repo. Prefer globFiles for finding files by name/extension and searchFiles for text inside files.";
350
+ }
351
+
352
+ function buildListWorkspaceParameters(variant?: ListFilesParameterVariant) {
353
+ const path = buildWorkspacePathProperty();
354
+ const _activity = buildActivityProperty();
355
+ const maxDepth = {
356
+ type: "integer",
357
+ description: "Maximum directory depth to include. Defaults to 1 for the requested directory only.",
358
+ };
359
+ const maxResults = {
360
+ type: "integer",
361
+ description: "Maximum number of entries to return. Use a small value for large directories.",
362
+ };
363
+ const entryType = {
364
+ type: "string",
365
+ enum: ["all", "files", "directories"],
366
+ description: "Filter returned entries. Defaults to all.",
367
+ };
368
+ if (variant === "minimal") {
369
+ return {
370
+ type: "object",
371
+ properties: { path, _activity },
372
+ };
373
+ }
374
+ if (variant === "rich") {
375
+ return {
376
+ type: "object",
377
+ properties: {
378
+ path,
379
+ maxDepth,
380
+ maxResults,
381
+ entryType,
382
+ _activity,
383
+ },
384
+ };
385
+ }
386
+ return {
387
+ type: "object",
388
+ properties: {
389
+ path,
390
+ maxResults,
391
+ _activity,
392
+ },
393
+ };
394
+ }
395
+
396
+ function buildListWorkspaceFilesTool(args?: {
397
+ descriptionVariant?: ListFilesDescriptionVariant;
398
+ parameterVariant?: ListFilesParameterVariant;
399
+ }): OpenAiCompatibleTool {
86
400
  return {
87
401
  type: "function",
88
402
  function: {
89
403
  name: "listFiles",
90
- description: "List files and directories inside a workspace directory.",
91
- parameters: {
92
- type: "object",
93
- properties: {
94
- path: buildWorkspacePathProperty(),
95
- },
404
+ description: buildListWorkspaceDescription(args?.descriptionVariant),
405
+ parameters: buildListWorkspaceParameters(args?.parameterVariant),
406
+ },
407
+ };
408
+ }
409
+
410
+ function buildReadWorkspaceDescription(variant?: ReadFileDescriptionVariant) {
411
+ if (variant === "brief") {
412
+ return "Read a UTF-8 text file inside the workspace.";
413
+ }
414
+ if (variant === "workflow") {
415
+ return "Read a UTF-8 text file inside the workspace after listFiles, globFiles, or searchFiles has narrowed the target. Use line ranges from searchFiles matches and tailLines for logs or generated output. Read the whole file only when the task truly needs all content or exact edit context.";
416
+ }
417
+ if (variant === "antiShell") {
418
+ return "Read a UTF-8 text file inside the workspace. Do not use execShell with cat/sed/head/tail for normal text reads when readFile can return the needed file or line range. Use searchFiles first for content search, then readFile only the relevant lines when possible.";
419
+ }
420
+ return "Read a UTF-8 text file inside the workspace. Read before editing when you need exact text for editFile. For discovery or classification tasks, avoid batch-reading every candidate; report candidate paths first, or read only one to three representative files with maxLines or focused line ranges. Use line ranges for large code, docs, data, configs, and logs after searchFiles returns line numbers. Read the whole file only when the task truly needs all content or exact edit context.";
421
+ }
422
+
423
+ function buildReadWorkspaceParameters(variant?: ReadFileParameterVariant) {
424
+ const path = buildWorkspacePathProperty();
425
+ const _activity = buildActivityProperty();
426
+ const startLine = {
427
+ type: "integer",
428
+ description: "1-based first line to return. Use with endLine or maxLines after searchFiles gives a line number.",
429
+ };
430
+ const endLine = {
431
+ type: "integer",
432
+ description: "1-based last line to return, inclusive.",
433
+ };
434
+ const maxLines = {
435
+ type: "integer",
436
+ description: "Maximum number of lines to return starting at startLine, or from the start of the file when startLine is omitted. Use for lightweight previews of candidate configs, docs, data, and logs instead of reading many whole files. When the user gives a read/preview budget, each readFile preview consumes one budget slot.",
437
+ };
438
+ const tailLines = {
439
+ type: "integer",
440
+ description: "Return only the last N lines, useful for logs and generated text.",
441
+ };
442
+ if (variant === "minimal") {
443
+ return {
444
+ type: "object",
445
+ properties: { path, _activity },
446
+ required: ["path"],
447
+ };
448
+ }
449
+ if (variant === "rich") {
450
+ return {
451
+ type: "object",
452
+ properties: {
453
+ path,
454
+ startLine,
455
+ endLine,
456
+ maxLines,
457
+ tailLines,
458
+ _activity,
96
459
  },
460
+ required: ["path"],
461
+ };
462
+ }
463
+ return {
464
+ type: "object",
465
+ properties: {
466
+ path,
467
+ startLine,
468
+ endLine,
469
+ maxLines,
470
+ _activity,
97
471
  },
472
+ required: ["path"],
98
473
  };
99
474
  }
100
475
 
101
- function buildReadWorkspaceFileTool(): OpenAiCompatibleTool {
476
+ function buildReadWorkspaceFileTool(args?: {
477
+ descriptionVariant?: ReadFileDescriptionVariant;
478
+ parameterVariant?: ReadFileParameterVariant;
479
+ }): OpenAiCompatibleTool {
102
480
  return {
103
481
  type: "function",
104
482
  function: {
105
483
  name: "readFile",
106
- description:
107
- "Read a UTF-8 text file inside the workspace. Read before editing when you need the current content or exact text for editFile; skip unnecessary reads for trivial known writes.",
108
- parameters: {
109
- type: "object",
110
- properties: {
111
- path: buildWorkspacePathProperty(),
112
- },
113
- required: ["path"],
114
- },
484
+ description: buildReadWorkspaceDescription(args?.descriptionVariant),
485
+ parameters: buildReadWorkspaceParameters(args?.parameterVariant),
115
486
  },
116
487
  };
117
488
  }
@@ -131,6 +502,7 @@ function buildWriteWorkspaceFileTool(): OpenAiCompatibleTool {
131
502
  type: "string",
132
503
  description: "Full UTF-8 file content to write.",
133
504
  },
505
+ _activity: buildActivityProperty(),
134
506
  },
135
507
  required: ["path", "content"],
136
508
  },
@@ -161,6 +533,7 @@ function buildReplaceWorkspaceTextTool(): OpenAiCompatibleTool {
161
533
  type: "integer",
162
534
  description: "Expected replacement count. Defaults to 1.",
163
535
  },
536
+ _activity: buildActivityProperty(),
164
537
  },
165
538
  required: ["path", "oldText", "newText"],
166
539
  },
@@ -168,28 +541,183 @@ function buildReplaceWorkspaceTextTool(): OpenAiCompatibleTool {
168
541
  };
169
542
  }
170
543
 
171
- function buildSearchWorkspaceTool(): OpenAiCompatibleTool {
544
+ function buildSearchWorkspaceDescription(variant?: SearchFilesDescriptionVariant) {
545
+ if (variant === "brief") {
546
+ return "Search workspace file contents.";
547
+ }
548
+ if (variant === "workflow") {
549
+ return "Search file contents inside the current workspace after listFiles or globFiles has scoped the likely area, or directly when the task names text to find. Use for code, docs, data, configs, logs, and other text files. Return line numbers, then use readFile with focused ranges for interpretation or editing.";
550
+ }
551
+ if (variant === "antiShell") {
552
+ return "Search text inside workspace files for code, docs, data, configs, logs, or other text-like assets. Do not use execShell for content search when searchFiles can directly find matching lines. Use globFiles instead for path-only discovery, then readFile for exact surrounding content when needed.";
553
+ }
554
+ return "Search file contents inside the current workspace using ripgrep when available. Use for grep-like text or regex search across code, docs, data, configs, logs, and other text files. Use globFiles instead when you only need to find files by name or extension before readFile.";
555
+ }
556
+
557
+ function buildSearchWorkspaceParameters(variant?: SearchFilesParameterVariant) {
558
+ const query = {
559
+ type: "string",
560
+ description: "Search query. Treated as a regular expression unless literal is true.",
561
+ };
562
+ const path = buildWorkspacePathProperty();
563
+ const includeIgnored = {
564
+ type: "boolean",
565
+ description:
566
+ "When true, search files ignored by .gitignore such as .tmp. Defaults to false; .git and node_modules remain excluded.",
567
+ };
568
+ const exclude = {
569
+ type: "array",
570
+ items: { type: "string" },
571
+ description: "Glob patterns to exclude from content search, for example ['dist/**', 'build/**', 'exports/**', '*.log'].",
572
+ };
573
+ const maxResults = {
574
+ type: "integer",
575
+ description: "Maximum number of matching output lines to return. Use a small value when you only need candidate files or examples.",
576
+ };
577
+ const literal = {
578
+ type: "boolean",
579
+ description: "When true, search for query as literal text instead of a regular expression.",
580
+ };
581
+ const caseSensitive = {
582
+ type: "boolean",
583
+ description: "When false, search case-insensitively. Defaults to true.",
584
+ };
585
+ const contextLines = {
586
+ type: "integer",
587
+ description: "Number of surrounding context lines to include around each match. Defaults to 0.",
588
+ };
589
+ if (variant === "minimal") {
590
+ return {
591
+ type: "object",
592
+ properties: { query, _activity: buildActivityProperty() },
593
+ required: ["query"],
594
+ };
595
+ }
596
+ if (variant === "rich") {
597
+ return {
598
+ type: "object",
599
+ properties: {
600
+ query,
601
+ path,
602
+ exclude,
603
+ includeIgnored,
604
+ maxResults,
605
+ literal,
606
+ caseSensitive,
607
+ contextLines,
608
+ _activity: buildActivityProperty(),
609
+ },
610
+ required: ["query"],
611
+ };
612
+ }
613
+ return {
614
+ type: "object",
615
+ properties: {
616
+ query,
617
+ path,
618
+ includeIgnored,
619
+ maxResults,
620
+ _activity: buildActivityProperty(),
621
+ },
622
+ required: ["query"],
623
+ };
624
+ }
625
+
626
+ function buildSearchWorkspaceTool(args?: {
627
+ descriptionVariant?: SearchFilesDescriptionVariant;
628
+ parameterVariant?: SearchFilesParameterVariant;
629
+ }): OpenAiCompatibleTool {
172
630
  return {
173
631
  type: "function",
174
632
  function: {
175
633
  name: "searchFiles",
176
- description: "Search text in the current workspace using ripgrep when available.",
177
- parameters: {
178
- type: "object",
179
- properties: {
180
- query: {
181
- type: "string",
182
- description: "Search query or regular expression.",
183
- },
184
- path: buildWorkspacePathProperty(),
185
- includeIgnored: {
186
- type: "boolean",
187
- description:
188
- "When true, search files ignored by .gitignore such as .tmp. Defaults to false; .git and node_modules remain excluded.",
189
- },
190
- },
191
- required: ["query"],
634
+ description: buildSearchWorkspaceDescription(args?.descriptionVariant),
635
+ parameters: buildSearchWorkspaceParameters(args?.parameterVariant),
636
+ },
637
+ };
638
+ }
639
+
640
+ function buildGlobWorkspaceDescription(variant?: GlobFilesDescriptionVariant) {
641
+ if (variant === "brief") {
642
+ return "Find workspace files by path glob without reading file contents.";
643
+ }
644
+ if (variant === "workflow") {
645
+ return "Find files by glob pattern inside the current workspace without reading file contents. Use after listFiles when you know the likely area, or first when the task names a filename, extension, asset type, config, or document pattern. Then use searchFiles for text inside candidates or readFile for the specific candidate paths.";
646
+ }
647
+ if (variant === "antiShell") {
648
+ return "Find workspace files by path glob without reading file contents. Use for code, docs, data, images, configs, logs, or any file discovery task. Do not use execShell or listFiles for path discovery when globFiles can directly narrow candidate files. Use before opening or reading files, and use searchFiles instead for searching text inside files.";
649
+ }
650
+ return "Find files by glob pattern inside the current workspace without reading file contents. Use for code, docs, data, images, configs, logs, or any file discovery task. Use one bounded glob with brace groups before repeated narrow globs when a task names several extensions or config names, for example **/*.{png,jpg,svg}, **/{package.json,bunfig.toml,tsconfig*.json}, or src/**/*.ts. Prefer searchFiles for searching text inside files, and readFile only after candidate paths are narrow enough.";
651
+ }
652
+
653
+ function buildGlobWorkspaceParameters(variant?: GlobFilesParameterVariant) {
654
+ const pattern = {
655
+ type: "string",
656
+ description: "Glob pattern for files. For candidate discovery, combine related names or extensions in one brace-group pattern before making repeated narrow calls, for example '**/*.ts', 'packages/**/local*.test.ts', '**/*.{png,jpg,svg}', or '**/{package.json,bunfig.toml,tsconfig*.json}'.",
657
+ };
658
+ const path = buildWorkspacePathProperty();
659
+ const includeIgnored = {
660
+ type: "boolean",
661
+ description:
662
+ "When true, include files ignored by .gitignore. Defaults to false; .git and node_modules remain excluded.",
663
+ };
664
+ const maxResults = {
665
+ type: "integer",
666
+ description: "Maximum number of file paths to return. Use a small value when you only need candidate paths before readFile; if results are truncated, report that and narrow the pattern or path before reading.",
667
+ };
668
+ const exclude = {
669
+ type: "array",
670
+ items: { type: "string" },
671
+ description: "Glob patterns to exclude from results, for example ['dist/**', 'build/**', 'exports/**', '*.tmp'].",
672
+ };
673
+ if (variant === "minimal") {
674
+ return {
675
+ type: "object",
676
+ properties: { pattern, _activity: buildActivityProperty() },
677
+ required: ["pattern"],
678
+ };
679
+ }
680
+ if (variant === "rich") {
681
+ return {
682
+ type: "object",
683
+ properties: {
684
+ pattern,
685
+ path,
686
+ exclude,
687
+ includeIgnored,
688
+ maxResults,
689
+ _activity: buildActivityProperty(),
192
690
  },
691
+ required: ["pattern"],
692
+ };
693
+ }
694
+ return {
695
+ type: "object",
696
+ properties: {
697
+ pattern,
698
+ glob: {
699
+ type: "string",
700
+ description: "Alias for pattern, kept for compatibility with codeSearch-style prompts.",
701
+ },
702
+ path,
703
+ exclude,
704
+ includeIgnored,
705
+ maxResults,
706
+ _activity: buildActivityProperty(),
707
+ },
708
+ };
709
+ }
710
+
711
+ function buildGlobWorkspaceFilesTool(args?: {
712
+ descriptionVariant?: GlobFilesDescriptionVariant;
713
+ parameterVariant?: GlobFilesParameterVariant;
714
+ }): OpenAiCompatibleTool {
715
+ return {
716
+ type: "function",
717
+ function: {
718
+ name: "globFiles",
719
+ description: buildGlobWorkspaceDescription(args?.descriptionVariant),
720
+ parameters: buildGlobWorkspaceParameters(args?.parameterVariant),
193
721
  },
194
722
  };
195
723
  }
@@ -277,6 +805,7 @@ function buildExecShellTool(toolName: string): OpenAiCompatibleTool {
277
805
  type: "string",
278
806
  description: "Shell command to run.",
279
807
  },
808
+ _activity: buildActivityProperty(),
280
809
  },
281
810
  },
282
811
  },
@@ -330,12 +859,27 @@ function buildWorkspaceShellCommand(args: {
330
859
  : buildBashCommand(args.command);
331
860
  }
332
861
 
333
- function buildWorkspaceToolDefinition(toolName: string) {
862
+ function buildWorkspaceToolDefinition(toolName: string, args?: {
863
+ listFilesDescriptionVariant?: ListFilesDescriptionVariant;
864
+ listFilesParameterVariant?: ListFilesParameterVariant;
865
+ readFileDescriptionVariant?: ReadFileDescriptionVariant;
866
+ readFileParameterVariant?: ReadFileParameterVariant;
867
+ globFilesDescriptionVariant?: GlobFilesDescriptionVariant;
868
+ globFilesParameterVariant?: GlobFilesParameterVariant;
869
+ searchFilesDescriptionVariant?: SearchFilesDescriptionVariant;
870
+ searchFilesParameterVariant?: SearchFilesParameterVariant;
871
+ }) {
334
872
  if (toolName === "listFiles") {
335
- return buildListWorkspaceFilesTool();
873
+ return buildListWorkspaceFilesTool({
874
+ descriptionVariant: args?.listFilesDescriptionVariant,
875
+ parameterVariant: args?.listFilesParameterVariant,
876
+ });
336
877
  }
337
878
  if (toolName === "readFile") {
338
- return buildReadWorkspaceFileTool();
879
+ return buildReadWorkspaceFileTool({
880
+ descriptionVariant: args?.readFileDescriptionVariant,
881
+ parameterVariant: args?.readFileParameterVariant,
882
+ });
339
883
  }
340
884
  if (toolName === "writeFile") {
341
885
  return buildWriteWorkspaceFileTool();
@@ -343,8 +887,17 @@ function buildWorkspaceToolDefinition(toolName: string) {
343
887
  if (toolName === "editFile") {
344
888
  return buildReplaceWorkspaceTextTool();
345
889
  }
890
+ if (toolName === "globFiles") {
891
+ return buildGlobWorkspaceFilesTool({
892
+ descriptionVariant: args?.globFilesDescriptionVariant,
893
+ parameterVariant: args?.globFilesParameterVariant,
894
+ });
895
+ }
346
896
  if (toolName === "searchFiles") {
347
- return buildSearchWorkspaceTool();
897
+ return buildSearchWorkspaceTool({
898
+ descriptionVariant: args?.searchFilesDescriptionVariant,
899
+ parameterVariant: args?.searchFilesParameterVariant,
900
+ });
348
901
  }
349
902
  if (toolName === "startPreview" || toolName === "getPreviewStatus" || toolName === "stopPreview" || toolName === "releasePreview") {
350
903
  return buildPreviewLifecycleTool(toolName);
@@ -368,8 +921,19 @@ function filterDeclaredWorkspaceToolNames(args: {
368
921
  export function buildLocalWorkspaceToolset(args: {
369
922
  declaredToolNames?: string[];
370
923
  exposeShellTools?: boolean;
924
+ useDeclaredToolNamesOnly?: boolean;
371
925
  }) {
372
926
  const exposeShellTools = args.exposeShellTools === true;
927
+ if (args.useDeclaredToolNamesOnly) {
928
+ const toolNames = new Set(filterDeclaredWorkspaceToolNames({
929
+ toolNames: args.declaredToolNames,
930
+ exposeShellTools,
931
+ }));
932
+ return {
933
+ toolNames: [...toolNames],
934
+ exposeShellTools,
935
+ };
936
+ }
373
937
  const toolNames = new Set([
374
938
  ...DEFAULT_LOCAL_CODING_TOOL_NAMES,
375
939
  ...(exposeShellTools ? SHELL_TOOL_NAMES : []),
@@ -387,7 +951,14 @@ export function buildLocalWorkspaceToolset(args: {
387
951
  export function buildLocalWorkspacePolicyToolNames(args: {
388
952
  declaredToolNames?: string[];
389
953
  exposeShellTools?: boolean;
954
+ useDeclaredToolNamesOnly?: boolean;
390
955
  }) {
956
+ if (args.useDeclaredToolNamesOnly) {
957
+ return filterDeclaredWorkspaceToolNames({
958
+ toolNames: args.declaredToolNames,
959
+ exposeShellTools: args.exposeShellTools === true,
960
+ });
961
+ }
391
962
  return [...new Set([
392
963
  ...buildLocalWorkspaceToolset({
393
964
  exposeShellTools: args.exposeShellTools,
@@ -402,6 +973,14 @@ export function buildLocalWorkspacePolicyToolNames(args: {
402
973
  export function buildLocalWorkspaceOpenAiTools(args: {
403
974
  toolNames?: string[];
404
975
  exposeShellTools?: boolean;
976
+ listFilesDescriptionVariant?: ListFilesDescriptionVariant;
977
+ listFilesParameterVariant?: ListFilesParameterVariant;
978
+ readFileDescriptionVariant?: ReadFileDescriptionVariant;
979
+ readFileParameterVariant?: ReadFileParameterVariant;
980
+ globFilesDescriptionVariant?: GlobFilesDescriptionVariant;
981
+ globFilesParameterVariant?: GlobFilesParameterVariant;
982
+ searchFilesDescriptionVariant?: SearchFilesDescriptionVariant;
983
+ searchFilesParameterVariant?: SearchFilesParameterVariant;
405
984
  }) {
406
985
  const declaredTools = new Set(args.toolNames ?? []);
407
986
  return WORKSPACE_TOOL_NAMES
@@ -412,7 +991,16 @@ export function buildLocalWorkspaceOpenAiTools(args: {
412
991
  }
413
992
  return true;
414
993
  })
415
- .map((toolName) => buildWorkspaceToolDefinition(toolName))
994
+ .map((toolName) => buildWorkspaceToolDefinition(toolName, {
995
+ listFilesDescriptionVariant: args.listFilesDescriptionVariant,
996
+ listFilesParameterVariant: args.listFilesParameterVariant,
997
+ readFileDescriptionVariant: args.readFileDescriptionVariant,
998
+ readFileParameterVariant: args.readFileParameterVariant,
999
+ globFilesDescriptionVariant: args.globFilesDescriptionVariant,
1000
+ globFilesParameterVariant: args.globFilesParameterVariant,
1001
+ searchFilesDescriptionVariant: args.searchFilesDescriptionVariant,
1002
+ searchFilesParameterVariant: args.searchFilesParameterVariant,
1003
+ }))
416
1004
  .filter((tool): tool is OpenAiCompatibleTool => Boolean(tool));
417
1005
  }
418
1006
 
@@ -477,6 +1065,96 @@ function readExpectedReplacementCount(args: WorkspaceFileArgs) {
477
1065
  return value;
478
1066
  }
479
1067
 
1068
+ function readPositiveIntegerArg(args: {
1069
+ value: unknown;
1070
+ name: string;
1071
+ max?: number;
1072
+ }) {
1073
+ if (args.value === undefined) return undefined;
1074
+ const value = Number(args.value);
1075
+ if (!Number.isInteger(value) || value < 1) {
1076
+ throw new Error(`${args.name} must be a positive integer.`);
1077
+ }
1078
+ return args.max ? Math.min(value, args.max) : value;
1079
+ }
1080
+
1081
+ function readFileSliceArgs(args: WorkspaceFileArgs) {
1082
+ const startLine = readPositiveIntegerArg({ value: args.startLine, name: "startLine" });
1083
+ const endLine = readPositiveIntegerArg({ value: args.endLine, name: "endLine" });
1084
+ const maxLines = readPositiveIntegerArg({ value: args.maxLines, name: "maxLines", max: 2000 });
1085
+ const tailLines = readPositiveIntegerArg({ value: args.tailLines, name: "tailLines", max: 2000 });
1086
+ if (tailLines !== undefined && (startLine !== undefined || endLine !== undefined)) {
1087
+ throw new Error("tailLines cannot be combined with startLine or endLine.");
1088
+ }
1089
+ if (endLine !== undefined && startLine !== undefined && endLine < startLine) {
1090
+ throw new Error("endLine must be greater than or equal to startLine.");
1091
+ }
1092
+ return {
1093
+ startLine,
1094
+ endLine,
1095
+ maxLines,
1096
+ tailLines,
1097
+ };
1098
+ }
1099
+
1100
+ function splitTextLines(content: string) {
1101
+ const lines = content.split(/\r?\n/);
1102
+ if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
1103
+ return lines;
1104
+ }
1105
+
1106
+ function sliceReadFileContent(args: {
1107
+ content: string;
1108
+ startLine?: number;
1109
+ endLine?: number;
1110
+ maxLines?: number;
1111
+ tailLines?: number;
1112
+ }) {
1113
+ const lines = splitTextLines(args.content);
1114
+ const totalLines = lines.length;
1115
+ if (
1116
+ args.startLine === undefined &&
1117
+ args.endLine === undefined &&
1118
+ args.maxLines === undefined &&
1119
+ args.tailLines === undefined
1120
+ ) {
1121
+ return {
1122
+ content: args.content,
1123
+ startLine: 1,
1124
+ endLine: totalLines,
1125
+ totalLines,
1126
+ truncated: false,
1127
+ };
1128
+ }
1129
+ if (args.tailLines !== undefined) {
1130
+ const startIndex = Math.max(0, totalLines - args.tailLines);
1131
+ const selected = lines.slice(startIndex);
1132
+ return {
1133
+ content: selected.join("\n"),
1134
+ startLine: startIndex + 1,
1135
+ endLine: startIndex + selected.length,
1136
+ totalLines,
1137
+ truncated: startIndex > 0,
1138
+ };
1139
+ }
1140
+ const startLine = args.startLine ?? 1;
1141
+ let endLine = args.endLine ?? totalLines;
1142
+ if (args.maxLines !== undefined) {
1143
+ endLine = Math.min(endLine, startLine + args.maxLines - 1);
1144
+ }
1145
+ const startIndex = Math.min(Math.max(startLine - 1, 0), totalLines);
1146
+ const endIndex = Math.min(Math.max(endLine, 0), totalLines);
1147
+ const selected = lines.slice(startIndex, endIndex);
1148
+ const effectiveEndLine = selected.length ? startIndex + selected.length : startIndex;
1149
+ return {
1150
+ content: selected.join("\n"),
1151
+ startLine,
1152
+ endLine: effectiveEndLine,
1153
+ totalLines,
1154
+ truncated: startLine > 1 || effectiveEndLine < totalLines,
1155
+ };
1156
+ }
1157
+
480
1158
  function countExactTextOccurrences(args: {
481
1159
  content: string;
482
1160
  oldText: string;
@@ -494,6 +1172,64 @@ function requireWorkspaceSearchQuery(args: WorkspaceFileArgs) {
494
1172
  return query;
495
1173
  }
496
1174
 
1175
+ function requireWorkspaceGlobPattern(args: WorkspaceFileArgs) {
1176
+ const pattern = typeof args.pattern === "string" && args.pattern.trim()
1177
+ ? args.pattern.trim()
1178
+ : typeof args.glob === "string" && args.glob.trim()
1179
+ ? args.glob.trim()
1180
+ : "";
1181
+ if (!pattern) throw new Error("globFiles requires a non-empty pattern.");
1182
+ return pattern;
1183
+ }
1184
+
1185
+ function readWorkspaceMaxResults(args: WorkspaceFileArgs) {
1186
+ if (args.maxResults === undefined) return undefined;
1187
+ const value = Number(args.maxResults);
1188
+ if (!Number.isInteger(value) || value < 1) {
1189
+ throw new Error("maxResults must be a positive integer.");
1190
+ }
1191
+ return Math.min(value, 500);
1192
+ }
1193
+
1194
+ function readWorkspaceMaxDepth(args: WorkspaceFileArgs) {
1195
+ if (args.maxDepth === undefined) return 1;
1196
+ const value = Number(args.maxDepth);
1197
+ if (!Number.isInteger(value) || value < 1) {
1198
+ throw new Error("maxDepth must be a positive integer.");
1199
+ }
1200
+ return Math.min(value, 10);
1201
+ }
1202
+
1203
+ function readWorkspaceEntryType(args: WorkspaceFileArgs) {
1204
+ if (args.entryType === undefined) return "all";
1205
+ if (args.entryType === "all" || args.entryType === "files" || args.entryType === "directories") {
1206
+ return args.entryType;
1207
+ }
1208
+ throw new Error("entryType must be one of all, files, or directories.");
1209
+ }
1210
+
1211
+ function readWorkspaceContextLines(args: WorkspaceFileArgs) {
1212
+ if (args.contextLines === undefined) return undefined;
1213
+ const value = Number(args.contextLines);
1214
+ if (!Number.isInteger(value) || value < 0) {
1215
+ throw new Error("contextLines must be a non-negative integer.");
1216
+ }
1217
+ return Math.min(value, 20);
1218
+ }
1219
+
1220
+ function readWorkspaceExcludeGlobs(args: WorkspaceFileArgs) {
1221
+ if (args.exclude === undefined) return [];
1222
+ if (typeof args.exclude === "string" && args.exclude.trim()) {
1223
+ return [args.exclude.trim()];
1224
+ }
1225
+ if (!Array.isArray(args.exclude)) {
1226
+ throw new Error("exclude must be a glob string or an array of glob strings.");
1227
+ }
1228
+ return args.exclude.flatMap((item) =>
1229
+ typeof item === "string" && item.trim() ? [item.trim()] : []
1230
+ );
1231
+ }
1232
+
497
1233
  function requireShellCommand(args: WorkspaceFileArgs, toolName: string) {
498
1234
  const command = typeof args.cmd === "string"
499
1235
  ? args.cmd.trim()
@@ -726,6 +1462,66 @@ async function runWorkspaceCommand(args: {
726
1462
  };
727
1463
  }
728
1464
 
1465
+ async function runWorkspaceCommandLimitedLines(args: {
1466
+ workspaceRoot: string;
1467
+ command: string[];
1468
+ maxLines: number;
1469
+ commandPrefix?: string[];
1470
+ }) {
1471
+ const command = [
1472
+ ...(args.commandPrefix ?? []),
1473
+ ...args.command,
1474
+ ];
1475
+ const proc = Bun.spawn(command, {
1476
+ cwd: resolve(args.workspaceRoot),
1477
+ stdout: "pipe",
1478
+ stderr: "pipe",
1479
+ stdin: "ignore",
1480
+ detached: false,
1481
+ });
1482
+ const stderrPromise = new Response(proc.stderr).text();
1483
+ const reader = proc.stdout.getReader();
1484
+ const decoder = new TextDecoder();
1485
+ const lines: string[] = [];
1486
+ let pending = "";
1487
+ let limitedByMaxResults = false;
1488
+ try {
1489
+ while (lines.length < args.maxLines) {
1490
+ const chunk = await reader.read();
1491
+ if (chunk.done) break;
1492
+ pending += decoder.decode(chunk.value, { stream: true });
1493
+ const parts = pending.split(/\r?\n/);
1494
+ pending = parts.pop() ?? "";
1495
+ for (const part of parts) {
1496
+ if (!part) continue;
1497
+ lines.push(part);
1498
+ if (lines.length >= args.maxLines) {
1499
+ limitedByMaxResults = true;
1500
+ break;
1501
+ }
1502
+ }
1503
+ }
1504
+ if (!limitedByMaxResults && pending.trim()) lines.push(pending);
1505
+ } finally {
1506
+ reader.releaseLock();
1507
+ }
1508
+ if (limitedByMaxResults) {
1509
+ try {
1510
+ proc.kill("SIGTERM");
1511
+ } catch {
1512
+ // The command may already have exited.
1513
+ }
1514
+ }
1515
+ const exitCode = limitedByMaxResults ? 0 : Number(await proc.exited);
1516
+ const stderr = await stderrPromise;
1517
+ return {
1518
+ stdout: lines.join("\n"),
1519
+ stderr,
1520
+ exitCode,
1521
+ limitedByMaxResults,
1522
+ };
1523
+ }
1524
+
729
1525
  export function resolveLocalWorkspaceToolPath(args: {
730
1526
  workspaceRoot: string;
731
1527
  requestedPath: string;
@@ -744,19 +1540,33 @@ async function readFileTool(args: {
744
1540
  }): Promise<AgentRuntimeToolResult> {
745
1541
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
746
1542
  const requestedPath = requireWorkspaceToolPath(parsed);
1543
+ const sliceArgs = readFileSliceArgs(parsed);
747
1544
  const absolutePath = resolveLocalWorkspaceToolPath({
748
1545
  workspaceRoot: args.workspaceRoot,
749
1546
  requestedPath,
750
1547
  });
751
1548
  const content = await readFile(absolutePath, "utf8");
752
- return {
1549
+ const activity = extractActivity(parsed);
1550
+ const sliced = sliceReadFileContent({
753
1551
  content,
1552
+ ...sliceArgs,
1553
+ });
1554
+ return {
1555
+ content: sliced.content,
754
1556
  metadata: {
755
1557
  path: normalizeWorkspaceRelativePath({
756
1558
  workspaceRoot: resolve(args.workspaceRoot),
757
1559
  targetPath: absolutePath,
758
1560
  }),
759
- bytes: Buffer.byteLength(content),
1561
+ bytes: Buffer.byteLength(sliced.content),
1562
+ totalBytes: Buffer.byteLength(content),
1563
+ startLine: sliced.startLine,
1564
+ endLine: sliced.endLine,
1565
+ totalLines: sliced.totalLines,
1566
+ truncated: sliced.truncated,
1567
+ ...(sliceArgs.maxLines ? { maxLines: sliceArgs.maxLines } : {}),
1568
+ ...(sliceArgs.tailLines ? { tailLines: sliceArgs.tailLines } : {}),
1569
+ ...(activity ? { activity } : {}),
760
1570
  },
761
1571
  };
762
1572
  }
@@ -783,11 +1593,13 @@ async function writeFileTool(args: {
783
1593
  workspaceRoot: resolve(args.workspaceRoot),
784
1594
  targetPath: absolutePath,
785
1595
  });
1596
+ const activity = extractActivity(parsed);
786
1597
  return {
787
1598
  content: `wrote ${relativePath}`,
788
1599
  metadata: {
789
1600
  path: relativePath,
790
1601
  bytes: Buffer.byteLength(content),
1602
+ ...(activity ? { activity } : {}),
791
1603
  },
792
1604
  };
793
1605
  }
@@ -819,12 +1631,14 @@ async function editFileTool(args: {
819
1631
  workspaceRoot: resolve(args.workspaceRoot),
820
1632
  targetPath: absolutePath,
821
1633
  });
1634
+ const activity = extractActivity(parsed);
822
1635
  return {
823
1636
  content: `replaced ${replacementCount} occurrence${replacementCount === 1 ? "" : "s"} in ${relativePath}`,
824
1637
  metadata: {
825
1638
  path: relativePath,
826
1639
  replacements: replacementCount,
827
1640
  bytes: Buffer.byteLength(nextContent),
1641
+ ...(activity ? { activity } : {}),
828
1642
  },
829
1643
  };
830
1644
  }
@@ -843,6 +1657,58 @@ async function formatWorkspaceDirEntry(args: {
843
1657
  return info.isDirectory() ? `${relativePath}/` : relativePath;
844
1658
  }
845
1659
 
1660
+ async function listWorkspaceEntries(args: {
1661
+ workspaceRoot: string;
1662
+ dirPath: string;
1663
+ maxDepth: number;
1664
+ entryType: "all" | "files" | "directories";
1665
+ maxResults?: number;
1666
+ }) {
1667
+ const entries: string[] = [];
1668
+ let truncated = false;
1669
+ let visitedEntries = 0;
1670
+ let limitedByMaxDepth = false;
1671
+ const visit = async (dirPath: string, depth: number) => {
1672
+ if (truncated) return;
1673
+ const names = (await readdir(dirPath)).sort((left, right) => left.localeCompare(right));
1674
+ for (const name of names) {
1675
+ if (truncated) break;
1676
+ const absolutePath = resolve(dirPath, name);
1677
+ const info = await stat(absolutePath);
1678
+ visitedEntries += 1;
1679
+ const relativePath = normalizeWorkspaceRelativePath({
1680
+ workspaceRoot: resolve(args.workspaceRoot),
1681
+ targetPath: absolutePath,
1682
+ });
1683
+ const isDirectory = info.isDirectory();
1684
+ if (
1685
+ args.entryType === "all" ||
1686
+ (args.entryType === "files" && !isDirectory) ||
1687
+ (args.entryType === "directories" && isDirectory)
1688
+ ) {
1689
+ entries.push(isDirectory ? `${relativePath}/` : relativePath);
1690
+ if (args.maxResults && entries.length >= args.maxResults) {
1691
+ truncated = true;
1692
+ break;
1693
+ }
1694
+ }
1695
+ if (isDirectory && depth < args.maxDepth) {
1696
+ await visit(absolutePath, depth + 1);
1697
+ } else if (isDirectory && depth >= args.maxDepth && !limitedByMaxDepth) {
1698
+ const childNames = await readdir(absolutePath);
1699
+ limitedByMaxDepth = childNames.length > 0;
1700
+ }
1701
+ }
1702
+ };
1703
+ await visit(args.dirPath, 1);
1704
+ return {
1705
+ entries,
1706
+ truncated,
1707
+ visitedEntries,
1708
+ limitedByMaxDepth,
1709
+ };
1710
+ }
1711
+
846
1712
  async function listFilesTool(args: {
847
1713
  call: AgentRuntimeToolCallInput;
848
1714
  workspaceRoot: string;
@@ -851,21 +1717,32 @@ async function listFilesTool(args: {
851
1717
  const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
852
1718
  ? parsed.path.trim()
853
1719
  : ".";
1720
+ const maxDepth = readWorkspaceMaxDepth(parsed);
1721
+ const maxResults = readWorkspaceMaxResults(parsed);
1722
+ const entryType = readWorkspaceEntryType(parsed);
854
1723
  const dirPath = resolveLocalWorkspaceToolPath({
855
1724
  workspaceRoot: args.workspaceRoot,
856
1725
  requestedPath,
857
1726
  });
858
- const names = (await readdir(dirPath)).sort((left, right) => left.localeCompare(right));
859
- const entries = await Promise.all(names.map((name) => formatWorkspaceDirEntry({
1727
+ const listed = await listWorkspaceEntries({
860
1728
  workspaceRoot: args.workspaceRoot,
861
1729
  dirPath,
862
- name,
863
- })));
1730
+ maxDepth,
1731
+ maxResults,
1732
+ entryType,
1733
+ });
864
1734
  return {
865
- content: entries.join("\n"),
1735
+ content: listed.entries.join("\n"),
866
1736
  metadata: {
867
1737
  path: requestedPath,
868
- count: entries.length,
1738
+ count: listed.entries.length,
1739
+ maxDepth,
1740
+ entryType,
1741
+ truncated: listed.truncated,
1742
+ limitedByMaxResults: listed.truncated,
1743
+ limitedByMaxDepth: listed.limitedByMaxDepth,
1744
+ visitedEntries: listed.visitedEntries,
1745
+ ...(maxResults ? { maxResults } : {}),
869
1746
  },
870
1747
  };
871
1748
  }
@@ -876,6 +1753,11 @@ async function searchFilesTool(args: {
876
1753
  }): Promise<AgentRuntimeToolResult> {
877
1754
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
878
1755
  const query = requireWorkspaceSearchQuery(parsed);
1756
+ const exclude = readWorkspaceExcludeGlobs(parsed);
1757
+ const maxResults = readWorkspaceMaxResults(parsed);
1758
+ const contextLines = readWorkspaceContextLines(parsed);
1759
+ const literal = parsed.literal === true;
1760
+ const caseSensitive = parsed.caseSensitive === false ? false : true;
879
1761
  const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
880
1762
  ? parsed.path.trim()
881
1763
  : ".";
@@ -888,47 +1770,336 @@ async function searchFilesTool(args: {
888
1770
  workspaceRoot: resolve(args.workspaceRoot),
889
1771
  targetPath: searchPath,
890
1772
  });
1773
+ const rgCommand = [
1774
+ "rg",
1775
+ "--line-number",
1776
+ "--no-heading",
1777
+ "--hidden",
1778
+ ...(literal ? ["--fixed-strings"] : []),
1779
+ ...(caseSensitive ? [] : ["--ignore-case"]),
1780
+ ...(contextLines !== undefined ? ["--context", String(contextLines)] : []),
1781
+ ...(includeIgnored ? ["--no-ignore"] : []),
1782
+ "--glob",
1783
+ "!node_modules",
1784
+ "--glob",
1785
+ "!.git",
1786
+ ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
1787
+ "--",
1788
+ query,
1789
+ relativeSearchPath,
1790
+ ];
1791
+ const grepCommand = [
1792
+ "grep",
1793
+ "-R",
1794
+ "-n",
1795
+ "-I",
1796
+ ...(literal ? ["-F"] : []),
1797
+ ...(caseSensitive ? [] : ["-i"]),
1798
+ ...(contextLines !== undefined ? ["-C", String(contextLines)] : []),
1799
+ "--exclude-dir=node_modules",
1800
+ "--exclude-dir=.git",
1801
+ ...exclude.flatMap((excludePattern) => ["--exclude", excludePattern]),
1802
+ query,
1803
+ relativeSearchPath,
1804
+ ];
891
1805
  const result = await (async () => {
892
1806
  try {
893
- return await runWorkspaceCommand({
894
- workspaceRoot: args.workspaceRoot,
895
- command: [
896
- "rg",
897
- "--line-number",
898
- "--no-heading",
899
- "--hidden",
900
- ...(includeIgnored ? ["--no-ignore"] : []),
901
- "--glob",
902
- "!node_modules",
903
- "--glob",
904
- "!.git",
905
- query,
906
- relativeSearchPath,
907
- ],
908
- });
1807
+ return maxResults && !contextLines
1808
+ ? await runWorkspaceCommandLimitedLines({
1809
+ workspaceRoot: args.workspaceRoot,
1810
+ command: rgCommand,
1811
+ maxLines: maxResults,
1812
+ })
1813
+ : await runWorkspaceCommand({
1814
+ workspaceRoot: args.workspaceRoot,
1815
+ command: rgCommand,
1816
+ });
909
1817
  } catch {
910
- return await runWorkspaceCommand({
911
- workspaceRoot: args.workspaceRoot,
912
- command: [
913
- "grep",
914
- "-R",
915
- "-n",
916
- "-I",
917
- "--exclude-dir=node_modules",
918
- "--exclude-dir=.git",
919
- query,
920
- relativeSearchPath,
921
- ],
922
- });
1818
+ return maxResults && !contextLines
1819
+ ? await runWorkspaceCommandLimitedLines({
1820
+ workspaceRoot: args.workspaceRoot,
1821
+ command: grepCommand,
1822
+ maxLines: maxResults,
1823
+ })
1824
+ : await runWorkspaceCommand({
1825
+ workspaceRoot: args.workspaceRoot,
1826
+ command: grepCommand,
1827
+ });
923
1828
  }
924
1829
  })();
1830
+ let outputLines = result.stdout
1831
+ .split(/\r?\n/)
1832
+ .map((line) => line.trimEnd().replace(/^\.\//, ""))
1833
+ .filter(Boolean);
1834
+ let limitedByMaxResults = "limitedByMaxResults" in result && result.limitedByMaxResults === true;
1835
+ if (outputLines.length === 0 && result.exitCode === 0 && (includeIgnored || !(await hasRootGitignore(args.workspaceRoot)))) {
1836
+ const fallback = await scanWorkspaceTextMatches({
1837
+ workspaceRoot: args.workspaceRoot,
1838
+ relativeSearchPath,
1839
+ query,
1840
+ exclude,
1841
+ maxResults,
1842
+ literal,
1843
+ caseSensitive,
1844
+ contextLines,
1845
+ });
1846
+ outputLines = fallback.lines;
1847
+ limitedByMaxResults = fallback.limitedByMaxResults;
1848
+ }
1849
+ if (contextLines && outputLines.length > 0) {
1850
+ const limited = limitSearchOutputByMatches(outputLines, maxResults);
1851
+ outputLines = limited.lines;
1852
+ limitedByMaxResults = limitedByMaxResults || limited.truncated;
1853
+ }
1854
+ const matchLines = outputLines.filter((line) => /:\d+:/.test(line));
1855
+ const matchedFiles = Array.from(new Set(matchLines.flatMap((line) => {
1856
+ const match = line.match(/^(.*?):\d+:/);
1857
+ return match?.[1] ? [match[1]] : [];
1858
+ })));
925
1859
  return {
926
- content: result.content,
1860
+ content: outputLines.join("\n"),
927
1861
  metadata: {
928
1862
  query,
929
1863
  path: requestedPath,
1864
+ searchedPath: relativeSearchPath,
1865
+ exclude,
930
1866
  includeIgnored,
1867
+ literal,
1868
+ caseSensitive,
1869
+ ...(contextLines === undefined ? {} : { contextLines }),
1870
+ count: outputLines.length,
1871
+ matchCount: matchLines.length,
1872
+ matchedFiles,
1873
+ truncated: limitedByMaxResults,
1874
+ limitedByMaxResults,
1875
+ ...(maxResults ? { maxResults } : {}),
931
1876
  exitCode: result.exitCode,
1877
+ ...(extractActivity(parsed) ? { activity: extractActivity(parsed) } : {}),
1878
+ },
1879
+ };
1880
+ }
1881
+
1882
+ async function hasRootGitignore(workspaceRoot: string) {
1883
+ try {
1884
+ await stat(resolve(workspaceRoot, ".gitignore"));
1885
+ return true;
1886
+ } catch {
1887
+ return false;
1888
+ }
1889
+ }
1890
+
1891
+ async function readRootGitignorePatterns(workspaceRoot: string) {
1892
+ try {
1893
+ const content = await readFile(resolve(workspaceRoot, ".gitignore"), "utf8");
1894
+ return content
1895
+ .split(/\r?\n/)
1896
+ .map((line) => line.trim())
1897
+ .filter((line) => line && !line.startsWith("#") && !line.startsWith("!"))
1898
+ .map((line) => {
1899
+ const unanchored = line.replace(/^\//, "");
1900
+ return unanchored.endsWith("/") ? `${unanchored}**` : unanchored;
1901
+ });
1902
+ } catch {
1903
+ return [];
1904
+ }
1905
+ }
1906
+
1907
+ async function filterRootGitignoredFiles(args: {
1908
+ workspaceRoot: string;
1909
+ files: string[];
1910
+ includeIgnored: boolean;
1911
+ }) {
1912
+ if (args.includeIgnored) return args.files;
1913
+ const patterns = await readRootGitignorePatterns(args.workspaceRoot);
1914
+ if (patterns.length === 0) return args.files;
1915
+ const globs = patterns.map((pattern) => new Bun.Glob(pattern));
1916
+ return args.files.filter((file) => !globs.some((glob) => glob.match(file)));
1917
+ }
1918
+
1919
+ function scanWorkspaceGlobFiles(args: {
1920
+ workspaceRoot: string;
1921
+ pattern: string;
1922
+ relativeSearchPath: string;
1923
+ exclude: string[];
1924
+ }) {
1925
+ const pathPrefix = args.relativeSearchPath === "."
1926
+ ? ""
1927
+ : `${args.relativeSearchPath.replace(/\/+$/, "")}/`;
1928
+ return Array.from(new Bun.Glob(args.pattern).scanSync({
1929
+ cwd: args.workspaceRoot,
1930
+ dot: true,
1931
+ onlyFiles: true,
1932
+ }))
1933
+ .map((line) => line.trim().replace(/^\.\//, ""))
1934
+ .filter(Boolean)
1935
+ .filter((line) => !line.startsWith(".git/") && !line.startsWith("node_modules/"))
1936
+ .filter((line) => !pathPrefix || line === args.relativeSearchPath || line.startsWith(pathPrefix))
1937
+ .filter((line) => !args.exclude.some((pattern) => new Bun.Glob(pattern).match(line)))
1938
+ .sort((left, right) => left.localeCompare(right));
1939
+ }
1940
+
1941
+ function limitSearchOutputByMatches(lines: string[], maxResults?: number) {
1942
+ if (!maxResults) return { lines, truncated: false };
1943
+ const limited: string[] = [];
1944
+ let matches = 0;
1945
+ for (const line of lines) {
1946
+ if (/:\d+:/.test(line)) {
1947
+ if (matches >= maxResults) {
1948
+ return { lines: limited, truncated: true };
1949
+ }
1950
+ matches += 1;
1951
+ }
1952
+ limited.push(line);
1953
+ }
1954
+ return { lines: limited, truncated: false };
1955
+ }
1956
+
1957
+ async function scanWorkspaceTextMatches(args: {
1958
+ workspaceRoot: string;
1959
+ relativeSearchPath: string;
1960
+ query: string;
1961
+ exclude: string[];
1962
+ maxResults?: number;
1963
+ literal: boolean;
1964
+ caseSensitive: boolean;
1965
+ contextLines?: number;
1966
+ }) {
1967
+ const files = scanWorkspaceGlobFiles({
1968
+ workspaceRoot: args.workspaceRoot,
1969
+ pattern: "**/*",
1970
+ relativeSearchPath: args.relativeSearchPath,
1971
+ exclude: args.exclude,
1972
+ });
1973
+ const results: string[] = [];
1974
+ const seenContext = new Set<string>();
1975
+ let matchCount = 0;
1976
+ const literalQuery = args.caseSensitive ? args.query : args.query.toLowerCase();
1977
+ const regex = args.literal
1978
+ ? undefined
1979
+ : new RegExp(args.query, args.caseSensitive ? "" : "i");
1980
+ const pushLine = (file: string, lineNumber: number, text: string, isMatch: boolean) => {
1981
+ const separator = isMatch ? ":" : "-";
1982
+ const key = `${file}:${lineNumber}:${separator}`;
1983
+ if (seenContext.has(key)) return;
1984
+ seenContext.add(key);
1985
+ results.push(`${file}${separator}${lineNumber}${separator}${text}`);
1986
+ };
1987
+ for (const file of files) {
1988
+ let content = "";
1989
+ try {
1990
+ content = await readFile(resolve(args.workspaceRoot, file), "utf8");
1991
+ } catch {
1992
+ continue;
1993
+ }
1994
+ if (content.includes("\u0000")) continue;
1995
+ const lines = content.split(/\r?\n/);
1996
+ for (let index = 0; index < lines.length; index += 1) {
1997
+ const line = lines[index] ?? "";
1998
+ const haystack = args.caseSensitive ? line : line.toLowerCase();
1999
+ const matched = args.literal
2000
+ ? haystack.includes(literalQuery)
2001
+ : regex?.test(line) === true;
2002
+ if (!matched) continue;
2003
+ matchCount += 1;
2004
+ const contextLines = args.contextLines ?? 0;
2005
+ const start = Math.max(0, index - contextLines);
2006
+ const end = Math.min(lines.length - 1, index + contextLines);
2007
+ for (let contextIndex = start; contextIndex <= end; contextIndex += 1) {
2008
+ pushLine(file, contextIndex + 1, lines[contextIndex] ?? "", contextIndex === index);
2009
+ }
2010
+ if (args.maxResults && matchCount >= args.maxResults) {
2011
+ return {
2012
+ lines: results,
2013
+ limitedByMaxResults: true,
2014
+ };
2015
+ }
2016
+ }
2017
+ }
2018
+ return {
2019
+ lines: results,
2020
+ limitedByMaxResults: false,
2021
+ };
2022
+ }
2023
+
2024
+ async function globFilesTool(args: {
2025
+ call: AgentRuntimeToolCallInput;
2026
+ workspaceRoot: string;
2027
+ }): Promise<AgentRuntimeToolResult> {
2028
+ const parsed = parseWorkspaceToolArguments(args.call.arguments);
2029
+ const pattern = requireWorkspaceGlobPattern(parsed);
2030
+ const exclude = readWorkspaceExcludeGlobs(parsed);
2031
+ const maxResults = readWorkspaceMaxResults(parsed);
2032
+ const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
2033
+ ? parsed.path.trim()
2034
+ : ".";
2035
+ const includeIgnored = parsed.includeIgnored === true;
2036
+ const searchPath = resolveLocalWorkspaceToolPath({
2037
+ workspaceRoot: args.workspaceRoot,
2038
+ requestedPath,
2039
+ });
2040
+ const relativeSearchPath = normalizeWorkspaceRelativePath({
2041
+ workspaceRoot: resolve(args.workspaceRoot),
2042
+ targetPath: searchPath,
2043
+ });
2044
+ const command = [
2045
+ "rg",
2046
+ "--files",
2047
+ "--hidden",
2048
+ ...(includeIgnored ? ["--no-ignore"] : []),
2049
+ "--glob",
2050
+ pattern,
2051
+ "--glob",
2052
+ "!node_modules",
2053
+ "--glob",
2054
+ "!.git",
2055
+ ...exclude.flatMap((excludePattern) => ["--glob", `!${excludePattern}`]),
2056
+ ...(relativeSearchPath === "." ? [] : [relativeSearchPath]),
2057
+ ];
2058
+ const result = await runWorkspaceCommand({
2059
+ workspaceRoot: args.workspaceRoot,
2060
+ command,
2061
+ });
2062
+ const pathPrefix = relativeSearchPath === "." ? "" : `${relativeSearchPath.replace(/\/+$/, "")}/`;
2063
+ let files = result.stdout
2064
+ .split(/\r?\n/)
2065
+ .map((line) => line.trim().replace(/^\.\//, ""))
2066
+ .filter(Boolean)
2067
+ .filter((line) => !pathPrefix || line === relativeSearchPath || line.startsWith(pathPrefix))
2068
+ .sort((left, right) => left.localeCompare(right));
2069
+ files = await filterRootGitignoredFiles({
2070
+ workspaceRoot: args.workspaceRoot,
2071
+ files,
2072
+ includeIgnored,
2073
+ });
2074
+ if (files.length === 0 && (includeIgnored || !(await hasRootGitignore(args.workspaceRoot)))) {
2075
+ files = scanWorkspaceGlobFiles({
2076
+ workspaceRoot: args.workspaceRoot,
2077
+ pattern,
2078
+ relativeSearchPath,
2079
+ exclude,
2080
+ });
2081
+ }
2082
+ const commandLimitedByMaxResults = result.limitedByMaxResults === true;
2083
+ const totalCount = commandLimitedByMaxResults ? undefined : files.length;
2084
+ const limitedFiles = maxResults ? files.slice(0, maxResults) : files;
2085
+ const limitedByMaxResults = commandLimitedByMaxResults || limitedFiles.length < (totalCount ?? limitedFiles.length);
2086
+ const activity = extractActivity(parsed);
2087
+ return {
2088
+ content: limitedFiles.join("\n"),
2089
+ metadata: {
2090
+ pattern,
2091
+ effectivePattern: pathPrefix ? `${pathPrefix}${pattern}` : pattern,
2092
+ path: requestedPath,
2093
+ searchedPath: relativeSearchPath,
2094
+ exclude,
2095
+ includeIgnored,
2096
+ count: limitedFiles.length,
2097
+ ...(totalCount === undefined ? {} : { totalCount }),
2098
+ truncated: limitedByMaxResults,
2099
+ limitedByMaxResults,
2100
+ ...(maxResults ? { maxResults } : {}),
2101
+ exitCode: result.exitCode,
2102
+ ...(activity ? { activity } : {}),
932
2103
  },
933
2104
  };
934
2105
  }
@@ -1062,9 +2233,14 @@ async function execShellTool(args: {
1062
2233
  outputLimit: args.commandOutputLimit,
1063
2234
  commandPrefix: args.commandPrefix,
1064
2235
  });
2236
+ const activity = extractActivity(parsed);
1065
2237
  return {
1066
2238
  content: result.content,
1067
- metadata: { exitCode: result.exitCode, timedOut: result.timedOut },
2239
+ metadata: {
2240
+ exitCode: result.exitCode,
2241
+ timedOut: result.timedOut,
2242
+ ...(activity ? { activity } : {}),
2243
+ },
1068
2244
  };
1069
2245
  }
1070
2246
 
@@ -1074,6 +2250,10 @@ export function createLocalWorkspaceToolExecutors(args: LocalWorkspaceToolArgs)
1074
2250
  call,
1075
2251
  workspaceRoot: args.workspaceRoot,
1076
2252
  }),
2253
+ globFiles: (call: AgentRuntimeToolCallInput) => globFilesTool({
2254
+ call,
2255
+ workspaceRoot: args.workspaceRoot,
2256
+ }),
1077
2257
  listFiles: (call: AgentRuntimeToolCallInput) => listFilesTool({
1078
2258
  call,
1079
2259
  workspaceRoot: args.workspaceRoot,