@xortex/xcode 3.0.0

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 (934) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -0
  3. package/bin/xcode +127 -0
  4. package/bin/xcode-test +84 -0
  5. package/bin/xcode.cmd +31 -0
  6. package/constants/apiLimits.ts +94 -0
  7. package/constants/betas.ts +52 -0
  8. package/constants/common.ts +33 -0
  9. package/constants/cyberRiskInstruction.ts +24 -0
  10. package/constants/errorIds.ts +15 -0
  11. package/constants/figures.ts +45 -0
  12. package/constants/files.ts +156 -0
  13. package/constants/github-app.ts +144 -0
  14. package/constants/keys.ts +11 -0
  15. package/constants/messages.ts +1 -0
  16. package/constants/oauth.ts +234 -0
  17. package/constants/outputStyles.ts +216 -0
  18. package/constants/product.ts +76 -0
  19. package/constants/prompts.ts +939 -0
  20. package/constants/spinnerVerbs.ts +204 -0
  21. package/constants/system.ts +95 -0
  22. package/constants/systemPromptSections.ts +68 -0
  23. package/constants/toolLimits.ts +56 -0
  24. package/constants/tools.ts +112 -0
  25. package/constants/turnCompletionVerbs.ts +12 -0
  26. package/constants/xml.ts +86 -0
  27. package/entrypoints/agentSdkTypes.ts +443 -0
  28. package/entrypoints/cli.tsx +307 -0
  29. package/entrypoints/init.ts +340 -0
  30. package/entrypoints/mcp.ts +196 -0
  31. package/entrypoints/sandboxTypes.ts +156 -0
  32. package/entrypoints/sdk/controlSchemas.ts +663 -0
  33. package/entrypoints/sdk/coreSchemas.ts +1889 -0
  34. package/entrypoints/sdk/coreTypes.generated.ts +2 -0
  35. package/entrypoints/sdk/coreTypes.ts +62 -0
  36. package/entrypoints/sdk/runtimeTypes.ts +140 -0
  37. package/entrypoints/sdk/sdkUtilityTypes.ts +3 -0
  38. package/entrypoints/sdk/toolTypes.ts +90 -0
  39. package/main.tsx +4686 -0
  40. package/package.json +120 -0
  41. package/services/AgentSummary/agentSummary.ts +179 -0
  42. package/services/MagicDocs/magicDocs.ts +254 -0
  43. package/services/MagicDocs/prompts.ts +127 -0
  44. package/services/PromptSuggestion/promptSuggestion.ts +523 -0
  45. package/services/PromptSuggestion/speculation.ts +991 -0
  46. package/services/SessionMemory/prompts.ts +324 -0
  47. package/services/SessionMemory/sessionMemory.ts +495 -0
  48. package/services/SessionMemory/sessionMemoryUtils.ts +207 -0
  49. package/services/analytics/config.ts +38 -0
  50. package/services/analytics/datadog.ts +307 -0
  51. package/services/analytics/firstPartyEventLogger.ts +449 -0
  52. package/services/analytics/firstPartyEventLoggingExporter.ts +806 -0
  53. package/services/analytics/growthbook.ts +1155 -0
  54. package/services/analytics/index.ts +173 -0
  55. package/services/analytics/metadata.ts +973 -0
  56. package/services/analytics/sink.ts +114 -0
  57. package/services/analytics/sinkKillswitch.ts +25 -0
  58. package/services/api/adminRequests.ts +119 -0
  59. package/services/api/bootstrap.ts +141 -0
  60. package/services/api/claude.ts +3422 -0
  61. package/services/api/client.ts +406 -0
  62. package/services/api/dumpPrompts.ts +226 -0
  63. package/services/api/emptyUsage.ts +22 -0
  64. package/services/api/errorUtils.ts +260 -0
  65. package/services/api/errors.ts +1207 -0
  66. package/services/api/filesApi.ts +748 -0
  67. package/services/api/firstTokenDate.ts +60 -0
  68. package/services/api/gemini.ts +359 -0
  69. package/services/api/geminiAdapter.ts +123 -0
  70. package/services/api/geminiClient.ts +291 -0
  71. package/services/api/grove.ts +357 -0
  72. package/services/api/logging.ts +788 -0
  73. package/services/api/metricsOptOut.ts +159 -0
  74. package/services/api/openRouterClient.ts +453 -0
  75. package/services/api/overageCreditGrant.ts +137 -0
  76. package/services/api/promptCacheBreakDetection.ts +727 -0
  77. package/services/api/referral.ts +281 -0
  78. package/services/api/sessionIngress.ts +514 -0
  79. package/services/api/ultrareviewQuota.ts +38 -0
  80. package/services/api/usage.ts +63 -0
  81. package/services/api/withRetry.ts +822 -0
  82. package/services/autoDream/autoDream.ts +324 -0
  83. package/services/autoDream/config.ts +21 -0
  84. package/services/autoDream/consolidationLock.ts +140 -0
  85. package/services/autoDream/consolidationPrompt.ts +65 -0
  86. package/services/awaySummary.ts +74 -0
  87. package/services/claudeAiLimits.ts +515 -0
  88. package/services/claudeAiLimitsHook.ts +23 -0
  89. package/services/compact/apiMicrocompact.ts +153 -0
  90. package/services/compact/autoCompact.ts +351 -0
  91. package/services/compact/compact.ts +1705 -0
  92. package/services/compact/compactWarningHook.ts +16 -0
  93. package/services/compact/compactWarningState.ts +18 -0
  94. package/services/compact/grouping.ts +63 -0
  95. package/services/compact/microCompact.ts +530 -0
  96. package/services/compact/postCompactCleanup.ts +77 -0
  97. package/services/compact/prompt.ts +374 -0
  98. package/services/compact/sessionMemoryCompact.ts +630 -0
  99. package/services/compact/timeBasedMCConfig.ts +43 -0
  100. package/services/diagnosticTracking.ts +397 -0
  101. package/services/extractMemories/extractMemories.ts +517 -0
  102. package/services/extractMemories/prompts.ts +154 -0
  103. package/services/internalLogging.ts +90 -0
  104. package/services/lsp/LSPClient.ts +447 -0
  105. package/services/lsp/LSPDiagnosticRegistry.ts +386 -0
  106. package/services/lsp/LSPServerInstance.ts +511 -0
  107. package/services/lsp/LSPServerManager.ts +420 -0
  108. package/services/lsp/config.ts +79 -0
  109. package/services/lsp/manager.ts +289 -0
  110. package/services/lsp/passiveFeedback.ts +328 -0
  111. package/services/mcp/InProcessTransport.ts +63 -0
  112. package/services/mcp/MCPConnectionManager.tsx +73 -0
  113. package/services/mcp/SdkControlTransport.ts +136 -0
  114. package/services/mcp/auth.ts +2465 -0
  115. package/services/mcp/channelAllowlist.ts +76 -0
  116. package/services/mcp/channelNotification.ts +316 -0
  117. package/services/mcp/channelPermissions.ts +240 -0
  118. package/services/mcp/claudeai.ts +164 -0
  119. package/services/mcp/client.ts +3348 -0
  120. package/services/mcp/config.ts +1578 -0
  121. package/services/mcp/elicitationHandler.ts +313 -0
  122. package/services/mcp/envExpansion.ts +38 -0
  123. package/services/mcp/headersHelper.ts +138 -0
  124. package/services/mcp/mcpStringUtils.ts +106 -0
  125. package/services/mcp/normalization.ts +23 -0
  126. package/services/mcp/oauthPort.ts +78 -0
  127. package/services/mcp/officialRegistry.ts +72 -0
  128. package/services/mcp/types.ts +258 -0
  129. package/services/mcp/useManageMCPConnections.ts +1141 -0
  130. package/services/mcp/utils.ts +575 -0
  131. package/services/mcp/vscodeSdkMcp.ts +112 -0
  132. package/services/mcp/xaa.ts +511 -0
  133. package/services/mcp/xaaIdpLogin.ts +487 -0
  134. package/services/mcpServerApproval.tsx +41 -0
  135. package/services/mockRateLimits.ts +882 -0
  136. package/services/notifier.ts +156 -0
  137. package/services/oauth/auth-code-listener.ts +211 -0
  138. package/services/oauth/client.ts +566 -0
  139. package/services/oauth/crypto.ts +23 -0
  140. package/services/oauth/getOauthProfile.ts +53 -0
  141. package/services/oauth/index.ts +198 -0
  142. package/services/plugins/PluginInstallationManager.ts +184 -0
  143. package/services/plugins/pluginCliCommands.ts +344 -0
  144. package/services/plugins/pluginOperations.ts +1088 -0
  145. package/services/policyLimits/index.ts +663 -0
  146. package/services/policyLimits/types.ts +27 -0
  147. package/services/preventSleep.ts +165 -0
  148. package/services/rateLimitMessages.ts +344 -0
  149. package/services/rateLimitMocking.ts +144 -0
  150. package/services/remoteManagedSettings/index.ts +638 -0
  151. package/services/remoteManagedSettings/securityCheck.tsx +74 -0
  152. package/services/remoteManagedSettings/syncCache.ts +112 -0
  153. package/services/remoteManagedSettings/syncCacheState.ts +96 -0
  154. package/services/remoteManagedSettings/types.ts +31 -0
  155. package/services/settingsSync/index.ts +581 -0
  156. package/services/settingsSync/types.ts +67 -0
  157. package/services/teamMemorySync/index.ts +1256 -0
  158. package/services/teamMemorySync/secretScanner.ts +324 -0
  159. package/services/teamMemorySync/teamMemSecretGuard.ts +44 -0
  160. package/services/teamMemorySync/types.ts +156 -0
  161. package/services/teamMemorySync/watcher.ts +387 -0
  162. package/services/tips/tipHistory.ts +17 -0
  163. package/services/tips/tipRegistry.ts +686 -0
  164. package/services/tips/tipScheduler.ts +58 -0
  165. package/services/tokenEstimation.ts +495 -0
  166. package/services/toolUseSummary/toolUseSummaryGenerator.ts +112 -0
  167. package/services/tools/StreamingToolExecutor.ts +530 -0
  168. package/services/tools/toolExecution.ts +1745 -0
  169. package/services/tools/toolHooks.ts +650 -0
  170. package/services/tools/toolOrchestration.ts +188 -0
  171. package/services/vcr.ts +406 -0
  172. package/services/voice.ts +525 -0
  173. package/services/voiceKeyterms.ts +106 -0
  174. package/services/voiceStreamSTT.ts +544 -0
  175. package/tools/AgentTool/AgentTool.tsx +1398 -0
  176. package/tools/AgentTool/UI.tsx +872 -0
  177. package/tools/AgentTool/agentColorManager.ts +66 -0
  178. package/tools/AgentTool/agentDisplay.ts +104 -0
  179. package/tools/AgentTool/agentMemory.ts +177 -0
  180. package/tools/AgentTool/agentMemorySnapshot.ts +197 -0
  181. package/tools/AgentTool/agentToolUtils.ts +686 -0
  182. package/tools/AgentTool/built-in/claudeCodeGuideAgent.ts +205 -0
  183. package/tools/AgentTool/built-in/exploreAgent.ts +83 -0
  184. package/tools/AgentTool/built-in/generalPurposeAgent.ts +34 -0
  185. package/tools/AgentTool/built-in/planAgent.ts +92 -0
  186. package/tools/AgentTool/built-in/statuslineSetup.ts +144 -0
  187. package/tools/AgentTool/built-in/verificationAgent.ts +152 -0
  188. package/tools/AgentTool/builtInAgents.ts +72 -0
  189. package/tools/AgentTool/constants.ts +12 -0
  190. package/tools/AgentTool/forkSubagent.ts +210 -0
  191. package/tools/AgentTool/loadAgentsDir.ts +755 -0
  192. package/tools/AgentTool/prompt.ts +287 -0
  193. package/tools/AgentTool/resumeAgent.ts +265 -0
  194. package/tools/AgentTool/runAgent.ts +973 -0
  195. package/tools/AskUserQuestionTool/AskUserQuestionTool.tsx +266 -0
  196. package/tools/AskUserQuestionTool/prompt.ts +44 -0
  197. package/tools/BashTool/BashTool.tsx +1144 -0
  198. package/tools/BashTool/BashToolResultMessage.tsx +191 -0
  199. package/tools/BashTool/UI.tsx +185 -0
  200. package/tools/BashTool/bashCommandHelpers.ts +265 -0
  201. package/tools/BashTool/bashPermissions.ts +2621 -0
  202. package/tools/BashTool/bashSecurity.ts +2592 -0
  203. package/tools/BashTool/commandSemantics.ts +140 -0
  204. package/tools/BashTool/commentLabel.ts +13 -0
  205. package/tools/BashTool/destructiveCommandWarning.ts +102 -0
  206. package/tools/BashTool/modeValidation.ts +115 -0
  207. package/tools/BashTool/pathValidation.ts +1303 -0
  208. package/tools/BashTool/prompt.ts +369 -0
  209. package/tools/BashTool/readOnlyValidation.ts +1990 -0
  210. package/tools/BashTool/sedEditParser.ts +322 -0
  211. package/tools/BashTool/sedValidation.ts +684 -0
  212. package/tools/BashTool/shouldUseSandbox.ts +153 -0
  213. package/tools/BashTool/toolName.ts +2 -0
  214. package/tools/BashTool/utils.ts +223 -0
  215. package/tools/BriefTool/BriefTool.ts +204 -0
  216. package/tools/BriefTool/UI.tsx +101 -0
  217. package/tools/BriefTool/attachments.ts +110 -0
  218. package/tools/BriefTool/prompt.ts +22 -0
  219. package/tools/BriefTool/upload.ts +174 -0
  220. package/tools/ConfigTool/ConfigTool.ts +467 -0
  221. package/tools/ConfigTool/UI.tsx +38 -0
  222. package/tools/ConfigTool/constants.ts +1 -0
  223. package/tools/ConfigTool/prompt.ts +93 -0
  224. package/tools/ConfigTool/supportedSettings.ts +211 -0
  225. package/tools/EnterPlanModeTool/EnterPlanModeTool.ts +126 -0
  226. package/tools/EnterPlanModeTool/UI.tsx +33 -0
  227. package/tools/EnterPlanModeTool/constants.ts +1 -0
  228. package/tools/EnterPlanModeTool/prompt.ts +170 -0
  229. package/tools/EnterWorktreeTool/EnterWorktreeTool.ts +127 -0
  230. package/tools/EnterWorktreeTool/UI.tsx +20 -0
  231. package/tools/EnterWorktreeTool/constants.ts +1 -0
  232. package/tools/EnterWorktreeTool/prompt.ts +30 -0
  233. package/tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts +493 -0
  234. package/tools/ExitPlanModeTool/UI.tsx +82 -0
  235. package/tools/ExitPlanModeTool/constants.ts +2 -0
  236. package/tools/ExitPlanModeTool/prompt.ts +29 -0
  237. package/tools/ExitWorktreeTool/ExitWorktreeTool.ts +329 -0
  238. package/tools/ExitWorktreeTool/UI.tsx +25 -0
  239. package/tools/ExitWorktreeTool/constants.ts +1 -0
  240. package/tools/ExitWorktreeTool/prompt.ts +32 -0
  241. package/tools/FileEditTool/FileEditTool.ts +625 -0
  242. package/tools/FileEditTool/UI.tsx +289 -0
  243. package/tools/FileEditTool/constants.ts +11 -0
  244. package/tools/FileEditTool/prompt.ts +28 -0
  245. package/tools/FileEditTool/types.ts +85 -0
  246. package/tools/FileEditTool/utils.ts +775 -0
  247. package/tools/FileReadTool/FileReadTool.ts +1183 -0
  248. package/tools/FileReadTool/UI.tsx +185 -0
  249. package/tools/FileReadTool/imageProcessor.ts +94 -0
  250. package/tools/FileReadTool/limits.ts +92 -0
  251. package/tools/FileReadTool/prompt.ts +49 -0
  252. package/tools/FileWriteTool/FileWriteTool.ts +434 -0
  253. package/tools/FileWriteTool/UI.tsx +405 -0
  254. package/tools/FileWriteTool/prompt.ts +18 -0
  255. package/tools/GlobTool/GlobTool.ts +198 -0
  256. package/tools/GlobTool/UI.tsx +63 -0
  257. package/tools/GlobTool/prompt.ts +7 -0
  258. package/tools/GrepTool/GrepTool.ts +577 -0
  259. package/tools/GrepTool/UI.tsx +201 -0
  260. package/tools/GrepTool/prompt.ts +18 -0
  261. package/tools/LSPTool/LSPTool.ts +860 -0
  262. package/tools/LSPTool/UI.tsx +228 -0
  263. package/tools/LSPTool/formatters.ts +592 -0
  264. package/tools/LSPTool/prompt.ts +21 -0
  265. package/tools/LSPTool/schemas.ts +215 -0
  266. package/tools/LSPTool/symbolContext.ts +90 -0
  267. package/tools/ListMcpResourcesTool/ListMcpResourcesTool.ts +123 -0
  268. package/tools/ListMcpResourcesTool/UI.tsx +29 -0
  269. package/tools/ListMcpResourcesTool/prompt.ts +20 -0
  270. package/tools/MCPTool/MCPTool.ts +77 -0
  271. package/tools/MCPTool/UI.tsx +403 -0
  272. package/tools/MCPTool/classifyForCollapse.ts +604 -0
  273. package/tools/MCPTool/prompt.ts +3 -0
  274. package/tools/McpAuthTool/McpAuthTool.ts +215 -0
  275. package/tools/NotebookEditTool/NotebookEditTool.ts +490 -0
  276. package/tools/NotebookEditTool/UI.tsx +93 -0
  277. package/tools/NotebookEditTool/constants.ts +2 -0
  278. package/tools/NotebookEditTool/prompt.ts +3 -0
  279. package/tools/PowerShellTool/PowerShellTool.tsx +1001 -0
  280. package/tools/PowerShellTool/UI.tsx +131 -0
  281. package/tools/PowerShellTool/clmTypes.ts +211 -0
  282. package/tools/PowerShellTool/commandSemantics.ts +142 -0
  283. package/tools/PowerShellTool/commonParameters.ts +30 -0
  284. package/tools/PowerShellTool/destructiveCommandWarning.ts +109 -0
  285. package/tools/PowerShellTool/gitSafety.ts +176 -0
  286. package/tools/PowerShellTool/modeValidation.ts +404 -0
  287. package/tools/PowerShellTool/pathValidation.ts +2049 -0
  288. package/tools/PowerShellTool/powershellPermissions.ts +1648 -0
  289. package/tools/PowerShellTool/powershellSecurity.ts +1090 -0
  290. package/tools/PowerShellTool/prompt.ts +145 -0
  291. package/tools/PowerShellTool/readOnlyValidation.ts +1823 -0
  292. package/tools/PowerShellTool/toolName.ts +2 -0
  293. package/tools/REPLTool/constants.ts +46 -0
  294. package/tools/REPLTool/primitiveTools.ts +39 -0
  295. package/tools/ReadMcpResourceTool/ReadMcpResourceTool.ts +158 -0
  296. package/tools/ReadMcpResourceTool/UI.tsx +37 -0
  297. package/tools/ReadMcpResourceTool/prompt.ts +16 -0
  298. package/tools/RemoteTriggerTool/RemoteTriggerTool.ts +161 -0
  299. package/tools/RemoteTriggerTool/UI.tsx +17 -0
  300. package/tools/RemoteTriggerTool/prompt.ts +15 -0
  301. package/tools/ScheduleCronTool/CronCreateTool.ts +157 -0
  302. package/tools/ScheduleCronTool/CronDeleteTool.ts +95 -0
  303. package/tools/ScheduleCronTool/CronListTool.ts +97 -0
  304. package/tools/ScheduleCronTool/UI.tsx +60 -0
  305. package/tools/ScheduleCronTool/prompt.ts +135 -0
  306. package/tools/SendMessageTool/SendMessageTool.ts +917 -0
  307. package/tools/SendMessageTool/UI.tsx +31 -0
  308. package/tools/SendMessageTool/constants.ts +1 -0
  309. package/tools/SendMessageTool/prompt.ts +49 -0
  310. package/tools/SkillTool/SkillTool.ts +1108 -0
  311. package/tools/SkillTool/UI.tsx +128 -0
  312. package/tools/SkillTool/constants.ts +1 -0
  313. package/tools/SkillTool/prompt.ts +241 -0
  314. package/tools/SleepTool/prompt.ts +17 -0
  315. package/tools/SyntheticOutputTool/SyntheticOutputTool.ts +163 -0
  316. package/tools/TaskCreateTool/TaskCreateTool.ts +138 -0
  317. package/tools/TaskCreateTool/constants.ts +1 -0
  318. package/tools/TaskCreateTool/prompt.ts +56 -0
  319. package/tools/TaskGetTool/TaskGetTool.ts +128 -0
  320. package/tools/TaskGetTool/constants.ts +1 -0
  321. package/tools/TaskGetTool/prompt.ts +24 -0
  322. package/tools/TaskListTool/TaskListTool.ts +116 -0
  323. package/tools/TaskListTool/constants.ts +1 -0
  324. package/tools/TaskListTool/prompt.ts +49 -0
  325. package/tools/TaskOutputTool/TaskOutputTool.tsx +584 -0
  326. package/tools/TaskOutputTool/constants.ts +1 -0
  327. package/tools/TaskStopTool/TaskStopTool.ts +131 -0
  328. package/tools/TaskStopTool/UI.tsx +41 -0
  329. package/tools/TaskStopTool/prompt.ts +8 -0
  330. package/tools/TaskUpdateTool/TaskUpdateTool.ts +406 -0
  331. package/tools/TaskUpdateTool/constants.ts +1 -0
  332. package/tools/TaskUpdateTool/prompt.ts +77 -0
  333. package/tools/TeamCreateTool/TeamCreateTool.ts +240 -0
  334. package/tools/TeamCreateTool/UI.tsx +6 -0
  335. package/tools/TeamCreateTool/constants.ts +1 -0
  336. package/tools/TeamCreateTool/prompt.ts +113 -0
  337. package/tools/TeamDeleteTool/TeamDeleteTool.ts +139 -0
  338. package/tools/TeamDeleteTool/UI.tsx +20 -0
  339. package/tools/TeamDeleteTool/constants.ts +1 -0
  340. package/tools/TeamDeleteTool/prompt.ts +16 -0
  341. package/tools/TodoWriteTool/TodoWriteTool.ts +115 -0
  342. package/tools/TodoWriteTool/constants.ts +1 -0
  343. package/tools/TodoWriteTool/prompt.ts +184 -0
  344. package/tools/ToolSearchTool/ToolSearchTool.ts +471 -0
  345. package/tools/ToolSearchTool/constants.ts +1 -0
  346. package/tools/ToolSearchTool/prompt.ts +121 -0
  347. package/tools/TungstenTool/TungstenTool.ts +4 -0
  348. package/tools/WebFetchTool/UI.tsx +72 -0
  349. package/tools/WebFetchTool/WebFetchTool.ts +318 -0
  350. package/tools/WebFetchTool/preapproved.ts +166 -0
  351. package/tools/WebFetchTool/prompt.ts +46 -0
  352. package/tools/WebFetchTool/utils.ts +530 -0
  353. package/tools/WebSearchTool/UI.tsx +101 -0
  354. package/tools/WebSearchTool/WebSearchTool.ts +435 -0
  355. package/tools/WebSearchTool/prompt.ts +34 -0
  356. package/tools/WorkflowTool/constants.ts +2 -0
  357. package/tools/XMemIngestTool/XMemIngestTool.ts +140 -0
  358. package/tools/XMemIngestTool/prompt.ts +13 -0
  359. package/tools/XMemRetrieveTool/XMemRetrieveTool.ts +177 -0
  360. package/tools/XMemRetrieveTool/prompt.ts +16 -0
  361. package/tools/XMemSearchTool/XMemSearchTool.ts +172 -0
  362. package/tools/XMemSearchTool/prompt.ts +11 -0
  363. package/tools/shared/gitOperationTracking.ts +277 -0
  364. package/tools/shared/spawnMultiAgent.ts +1093 -0
  365. package/tools/testing/TestingPermissionTool.tsx +74 -0
  366. package/tools/utils.ts +40 -0
  367. package/utils/CircularBuffer.ts +84 -0
  368. package/utils/Cursor.ts +1530 -0
  369. package/utils/QueryGuard.ts +121 -0
  370. package/utils/Shell.ts +474 -0
  371. package/utils/ShellCommand.ts +465 -0
  372. package/utils/abortController.ts +99 -0
  373. package/utils/activityManager.ts +164 -0
  374. package/utils/advisor.ts +145 -0
  375. package/utils/agentContext.ts +178 -0
  376. package/utils/agentId.ts +99 -0
  377. package/utils/agentSwarmsEnabled.ts +44 -0
  378. package/utils/agenticSessionSearch.ts +307 -0
  379. package/utils/analyzeContext.ts +1382 -0
  380. package/utils/ansiToPng.ts +334 -0
  381. package/utils/ansiToSvg.ts +272 -0
  382. package/utils/api.ts +718 -0
  383. package/utils/apiPreconnect.ts +71 -0
  384. package/utils/appleTerminalBackup.ts +124 -0
  385. package/utils/argumentSubstitution.ts +145 -0
  386. package/utils/array.ts +13 -0
  387. package/utils/asciicast.ts +239 -0
  388. package/utils/attachments.ts +4091 -0
  389. package/utils/attribution.ts +393 -0
  390. package/utils/auth.ts +2002 -0
  391. package/utils/authFileDescriptor.ts +196 -0
  392. package/utils/authPortable.ts +19 -0
  393. package/utils/autoModeDenials.ts +26 -0
  394. package/utils/autoRunIssue.tsx +122 -0
  395. package/utils/autoUpdater.ts +561 -0
  396. package/utils/aws.ts +74 -0
  397. package/utils/awsAuthStatusManager.ts +81 -0
  398. package/utils/axios.ts +8 -0
  399. package/utils/background/remote/preconditions.ts +235 -0
  400. package/utils/background/remote/remoteSession.ts +98 -0
  401. package/utils/backgroundHousekeeping.ts +94 -0
  402. package/utils/bash/ParsedCommand.ts +318 -0
  403. package/utils/bash/ShellSnapshot.ts +582 -0
  404. package/utils/bash/ast.ts +2679 -0
  405. package/utils/bash/bashParser.ts +4436 -0
  406. package/utils/bash/bashPipeCommand.ts +294 -0
  407. package/utils/bash/commands.ts +1339 -0
  408. package/utils/bash/heredoc.ts +733 -0
  409. package/utils/bash/parser.ts +230 -0
  410. package/utils/bash/prefix.ts +204 -0
  411. package/utils/bash/registry.ts +53 -0
  412. package/utils/bash/shellCompletion.ts +259 -0
  413. package/utils/bash/shellPrefix.ts +28 -0
  414. package/utils/bash/shellQuote.ts +304 -0
  415. package/utils/bash/shellQuoting.ts +128 -0
  416. package/utils/bash/specs/alias.ts +14 -0
  417. package/utils/bash/specs/index.ts +18 -0
  418. package/utils/bash/specs/nohup.ts +13 -0
  419. package/utils/bash/specs/pyright.ts +91 -0
  420. package/utils/bash/specs/sleep.ts +13 -0
  421. package/utils/bash/specs/srun.ts +31 -0
  422. package/utils/bash/specs/time.ts +13 -0
  423. package/utils/bash/specs/timeout.ts +20 -0
  424. package/utils/bash/treeSitterAnalysis.ts +506 -0
  425. package/utils/betas.ts +434 -0
  426. package/utils/billing.ts +78 -0
  427. package/utils/binaryCheck.ts +53 -0
  428. package/utils/browser.ts +68 -0
  429. package/utils/bufferedWriter.ts +100 -0
  430. package/utils/bundledMode.ts +22 -0
  431. package/utils/caCerts.ts +115 -0
  432. package/utils/caCertsConfig.ts +88 -0
  433. package/utils/cachePaths.ts +38 -0
  434. package/utils/classifierApprovals.ts +88 -0
  435. package/utils/classifierApprovalsHook.ts +17 -0
  436. package/utils/claudeCodeHints.ts +193 -0
  437. package/utils/claudeDesktop.ts +152 -0
  438. package/utils/claudeInChrome/chromeNativeHost.ts +527 -0
  439. package/utils/claudeInChrome/common.ts +540 -0
  440. package/utils/claudeInChrome/mcpServer.ts +292 -0
  441. package/utils/claudeInChrome/prompt.ts +83 -0
  442. package/utils/claudeInChrome/setup.ts +400 -0
  443. package/utils/claudeInChrome/setupPortable.ts +233 -0
  444. package/utils/claudeInChrome/toolRendering.tsx +262 -0
  445. package/utils/claudemd.ts +1479 -0
  446. package/utils/cleanup.ts +602 -0
  447. package/utils/cleanupRegistry.ts +25 -0
  448. package/utils/cliArgs.ts +60 -0
  449. package/utils/cliHighlight.ts +54 -0
  450. package/utils/codeIndexing.ts +206 -0
  451. package/utils/collapseBackgroundBashNotifications.ts +84 -0
  452. package/utils/collapseHookSummaries.ts +59 -0
  453. package/utils/collapseReadSearch.ts +1109 -0
  454. package/utils/collapseTeammateShutdowns.ts +55 -0
  455. package/utils/color-diff-mock.ts +27 -0
  456. package/utils/combinedAbortSignal.ts +47 -0
  457. package/utils/commandLifecycle.ts +21 -0
  458. package/utils/commitAttribution.ts +961 -0
  459. package/utils/completionCache.ts +166 -0
  460. package/utils/computerUse/appNames.ts +196 -0
  461. package/utils/computerUse/cleanup.ts +86 -0
  462. package/utils/computerUse/common.ts +61 -0
  463. package/utils/computerUse/computerUseLock.ts +215 -0
  464. package/utils/computerUse/drainRunLoop.ts +79 -0
  465. package/utils/computerUse/escHotkey.ts +54 -0
  466. package/utils/computerUse/executor.ts +658 -0
  467. package/utils/computerUse/gates.ts +72 -0
  468. package/utils/computerUse/hostAdapter.ts +69 -0
  469. package/utils/computerUse/inputLoader.ts +30 -0
  470. package/utils/computerUse/mcpServer.ts +106 -0
  471. package/utils/computerUse/setup.ts +53 -0
  472. package/utils/computerUse/swiftLoader.ts +23 -0
  473. package/utils/computerUse/toolRendering.tsx +125 -0
  474. package/utils/computerUse/wrapper.tsx +336 -0
  475. package/utils/concurrentSessions.ts +204 -0
  476. package/utils/config.ts +1817 -0
  477. package/utils/configConstants.ts +21 -0
  478. package/utils/contentArray.ts +51 -0
  479. package/utils/context.ts +221 -0
  480. package/utils/contextAnalysis.ts +272 -0
  481. package/utils/contextSuggestions.ts +235 -0
  482. package/utils/controlMessageCompat.ts +32 -0
  483. package/utils/conversationRecovery.ts +597 -0
  484. package/utils/cron.ts +308 -0
  485. package/utils/cronJitterConfig.ts +75 -0
  486. package/utils/cronScheduler.ts +565 -0
  487. package/utils/cronTasks.ts +458 -0
  488. package/utils/cronTasksLock.ts +195 -0
  489. package/utils/crossProjectResume.ts +75 -0
  490. package/utils/crypto.ts +13 -0
  491. package/utils/cwd.ts +32 -0
  492. package/utils/debug.ts +268 -0
  493. package/utils/debugFilter.ts +157 -0
  494. package/utils/deepLink/banner.ts +123 -0
  495. package/utils/deepLink/parseDeepLink.ts +170 -0
  496. package/utils/deepLink/protocolHandler.ts +136 -0
  497. package/utils/deepLink/registerProtocol.ts +348 -0
  498. package/utils/deepLink/terminalLauncher.ts +557 -0
  499. package/utils/deepLink/terminalPreference.ts +54 -0
  500. package/utils/desktopDeepLink.ts +236 -0
  501. package/utils/detectRepository.ts +178 -0
  502. package/utils/diagLogs.ts +94 -0
  503. package/utils/diff.ts +177 -0
  504. package/utils/directMemberMessage.ts +69 -0
  505. package/utils/displayTags.ts +51 -0
  506. package/utils/doctorContextWarnings.ts +265 -0
  507. package/utils/doctorDiagnostic.ts +625 -0
  508. package/utils/dxt/helpers.ts +88 -0
  509. package/utils/dxt/zip.ts +226 -0
  510. package/utils/earlyInput.ts +191 -0
  511. package/utils/editor.ts +183 -0
  512. package/utils/effort.ts +329 -0
  513. package/utils/embeddedTools.ts +29 -0
  514. package/utils/env.ts +347 -0
  515. package/utils/envDynamic.ts +151 -0
  516. package/utils/envUtils.ts +183 -0
  517. package/utils/envValidation.ts +38 -0
  518. package/utils/errorLogSink.ts +235 -0
  519. package/utils/errors.ts +238 -0
  520. package/utils/exampleCommands.ts +184 -0
  521. package/utils/execFileNoThrow.ts +150 -0
  522. package/utils/execFileNoThrowPortable.ts +89 -0
  523. package/utils/execSyncWrapper.ts +38 -0
  524. package/utils/exportRenderer.tsx +98 -0
  525. package/utils/extraUsage.ts +23 -0
  526. package/utils/fastMode.ts +532 -0
  527. package/utils/file.ts +584 -0
  528. package/utils/fileHistory.ts +1115 -0
  529. package/utils/fileOperationAnalytics.ts +71 -0
  530. package/utils/filePersistence/filePersistence.ts +287 -0
  531. package/utils/filePersistence/outputsScanner.ts +126 -0
  532. package/utils/fileRead.ts +102 -0
  533. package/utils/fileReadCache.ts +96 -0
  534. package/utils/fileStateCache.ts +142 -0
  535. package/utils/findExecutable.ts +17 -0
  536. package/utils/fingerprint.ts +76 -0
  537. package/utils/forkedAgent.ts +689 -0
  538. package/utils/format.ts +308 -0
  539. package/utils/formatBriefTimestamp.ts +81 -0
  540. package/utils/fpsTracker.ts +47 -0
  541. package/utils/frontmatterParser.ts +370 -0
  542. package/utils/fsOperations.ts +770 -0
  543. package/utils/fullscreen.ts +202 -0
  544. package/utils/generatedFiles.ts +136 -0
  545. package/utils/generators.ts +88 -0
  546. package/utils/genericProcessUtils.ts +184 -0
  547. package/utils/getWorktreePaths.ts +70 -0
  548. package/utils/getWorktreePathsPortable.ts +27 -0
  549. package/utils/ghPrStatus.ts +106 -0
  550. package/utils/git/gitConfigParser.ts +277 -0
  551. package/utils/git/gitFilesystem.ts +699 -0
  552. package/utils/git/gitignore.ts +99 -0
  553. package/utils/git.ts +926 -0
  554. package/utils/gitDiff.ts +532 -0
  555. package/utils/gitSettings.ts +18 -0
  556. package/utils/github/ghAuthStatus.ts +29 -0
  557. package/utils/githubRepoPathMapping.ts +162 -0
  558. package/utils/glob.ts +130 -0
  559. package/utils/gracefulShutdown.ts +529 -0
  560. package/utils/groupToolUses.ts +182 -0
  561. package/utils/handlePromptSubmit.ts +610 -0
  562. package/utils/hash.ts +46 -0
  563. package/utils/headlessProfiler.ts +178 -0
  564. package/utils/heapDumpService.ts +303 -0
  565. package/utils/heatmap.ts +198 -0
  566. package/utils/highlightMatch.tsx +28 -0
  567. package/utils/hooks/AsyncHookRegistry.ts +309 -0
  568. package/utils/hooks/apiQueryHookHelper.ts +141 -0
  569. package/utils/hooks/execAgentHook.ts +339 -0
  570. package/utils/hooks/execHttpHook.ts +242 -0
  571. package/utils/hooks/execPromptHook.ts +211 -0
  572. package/utils/hooks/fileChangedWatcher.ts +191 -0
  573. package/utils/hooks/hookEvents.ts +192 -0
  574. package/utils/hooks/hookHelpers.ts +83 -0
  575. package/utils/hooks/hooksConfigManager.ts +400 -0
  576. package/utils/hooks/hooksConfigSnapshot.ts +133 -0
  577. package/utils/hooks/hooksSettings.ts +271 -0
  578. package/utils/hooks/postSamplingHooks.ts +70 -0
  579. package/utils/hooks/registerFrontmatterHooks.ts +67 -0
  580. package/utils/hooks/registerSkillHooks.ts +64 -0
  581. package/utils/hooks/sessionHooks.ts +447 -0
  582. package/utils/hooks/skillImprovement.ts +267 -0
  583. package/utils/hooks/ssrfGuard.ts +294 -0
  584. package/utils/hooks.ts +5022 -0
  585. package/utils/horizontalScroll.ts +137 -0
  586. package/utils/http.ts +136 -0
  587. package/utils/hyperlink.ts +39 -0
  588. package/utils/iTermBackup.ts +73 -0
  589. package/utils/ide.ts +1494 -0
  590. package/utils/idePathConversion.ts +90 -0
  591. package/utils/idleTimeout.ts +53 -0
  592. package/utils/imagePaste.ts +416 -0
  593. package/utils/imageResizer.ts +880 -0
  594. package/utils/imageStore.ts +167 -0
  595. package/utils/imageValidation.ts +104 -0
  596. package/utils/immediateCommand.ts +15 -0
  597. package/utils/inProcessTeammateHelpers.ts +102 -0
  598. package/utils/ink.ts +26 -0
  599. package/utils/intl.ts +94 -0
  600. package/utils/jetbrains.ts +191 -0
  601. package/utils/json.ts +277 -0
  602. package/utils/jsonRead.ts +16 -0
  603. package/utils/keyboardShortcuts.ts +14 -0
  604. package/utils/lazySchema.ts +8 -0
  605. package/utils/listSessionsImpl.ts +454 -0
  606. package/utils/localInstaller.ts +162 -0
  607. package/utils/lockfile.ts +43 -0
  608. package/utils/log.ts +362 -0
  609. package/utils/logoV2Utils.ts +347 -0
  610. package/utils/mailbox.ts +73 -0
  611. package/utils/managedEnv.ts +199 -0
  612. package/utils/managedEnvConstants.ts +191 -0
  613. package/utils/markdown.ts +381 -0
  614. package/utils/markdownConfigLoader.ts +600 -0
  615. package/utils/mcp/dateTimeParser.ts +121 -0
  616. package/utils/mcp/elicitationValidation.ts +336 -0
  617. package/utils/mcpInstructionsDelta.ts +130 -0
  618. package/utils/mcpOutputStorage.ts +189 -0
  619. package/utils/mcpValidation.ts +208 -0
  620. package/utils/mcpWebSocketTransport.ts +200 -0
  621. package/utils/memoize.ts +269 -0
  622. package/utils/memory/types.ts +12 -0
  623. package/utils/memory/versions.ts +8 -0
  624. package/utils/memoryFileDetection.ts +289 -0
  625. package/utils/messagePredicates.ts +8 -0
  626. package/utils/messageQueueManager.ts +547 -0
  627. package/utils/messages/mappers.ts +290 -0
  628. package/utils/messages/systemInit.ts +96 -0
  629. package/utils/messages.ts +5520 -0
  630. package/utils/model/agent.ts +157 -0
  631. package/utils/model/aliases.ts +35 -0
  632. package/utils/model/antModels.ts +64 -0
  633. package/utils/model/bedrock.ts +265 -0
  634. package/utils/model/check1mAccess.ts +72 -0
  635. package/utils/model/configs.ts +158 -0
  636. package/utils/model/contextWindowUpgradeCheck.ts +47 -0
  637. package/utils/model/deprecation.ts +101 -0
  638. package/utils/model/model.ts +654 -0
  639. package/utils/model/modelAllowlist.ts +170 -0
  640. package/utils/model/modelCapabilities.ts +118 -0
  641. package/utils/model/modelOptions.ts +589 -0
  642. package/utils/model/modelStrings.ts +170 -0
  643. package/utils/model/modelSupportOverrides.ts +50 -0
  644. package/utils/model/providers.ts +42 -0
  645. package/utils/model/validateModel.ts +159 -0
  646. package/utils/modelCost.ts +231 -0
  647. package/utils/modifiers.ts +36 -0
  648. package/utils/mtls.ts +179 -0
  649. package/utils/nativeInstaller/download.ts +523 -0
  650. package/utils/nativeInstaller/index.ts +18 -0
  651. package/utils/nativeInstaller/installer.ts +1708 -0
  652. package/utils/nativeInstaller/packageManagers.ts +336 -0
  653. package/utils/nativeInstaller/pidLock.ts +433 -0
  654. package/utils/notebook.ts +224 -0
  655. package/utils/objectGroupBy.ts +18 -0
  656. package/utils/pasteStore.ts +104 -0
  657. package/utils/path.ts +155 -0
  658. package/utils/pdf.ts +300 -0
  659. package/utils/pdfUtils.ts +70 -0
  660. package/utils/peerAddress.ts +21 -0
  661. package/utils/permissions/PermissionMode.ts +141 -0
  662. package/utils/permissions/PermissionPromptToolResultSchema.ts +127 -0
  663. package/utils/permissions/PermissionResult.ts +35 -0
  664. package/utils/permissions/PermissionRule.ts +40 -0
  665. package/utils/permissions/PermissionUpdate.ts +389 -0
  666. package/utils/permissions/PermissionUpdateSchema.ts +78 -0
  667. package/utils/permissions/autoModeState.ts +39 -0
  668. package/utils/permissions/bashClassifier.ts +61 -0
  669. package/utils/permissions/bypassPermissionsKillswitch.ts +155 -0
  670. package/utils/permissions/classifierDecision.ts +98 -0
  671. package/utils/permissions/classifierShared.ts +39 -0
  672. package/utils/permissions/dangerousPatterns.ts +80 -0
  673. package/utils/permissions/denialTracking.ts +45 -0
  674. package/utils/permissions/filesystem.ts +1777 -0
  675. package/utils/permissions/getNextPermissionMode.ts +101 -0
  676. package/utils/permissions/pathValidation.ts +485 -0
  677. package/utils/permissions/permissionExplainer.ts +250 -0
  678. package/utils/permissions/permissionRuleParser.ts +198 -0
  679. package/utils/permissions/permissionSetup.ts +1532 -0
  680. package/utils/permissions/permissions.ts +1486 -0
  681. package/utils/permissions/permissionsLoader.ts +296 -0
  682. package/utils/permissions/shadowedRuleDetection.ts +234 -0
  683. package/utils/permissions/shellRuleMatching.ts +228 -0
  684. package/utils/permissions/yoloClassifier.ts +1495 -0
  685. package/utils/planModeV2.ts +95 -0
  686. package/utils/plans.ts +397 -0
  687. package/utils/platform.ts +150 -0
  688. package/utils/plugins/addDirPluginSettings.ts +71 -0
  689. package/utils/plugins/cacheUtils.ts +196 -0
  690. package/utils/plugins/dependencyResolver.ts +305 -0
  691. package/utils/plugins/fetchTelemetry.ts +135 -0
  692. package/utils/plugins/gitAvailability.ts +69 -0
  693. package/utils/plugins/headlessPluginInstall.ts +174 -0
  694. package/utils/plugins/hintRecommendation.ts +164 -0
  695. package/utils/plugins/installCounts.ts +292 -0
  696. package/utils/plugins/installedPluginsManager.ts +1268 -0
  697. package/utils/plugins/loadPluginAgents.ts +348 -0
  698. package/utils/plugins/loadPluginCommands.ts +946 -0
  699. package/utils/plugins/loadPluginHooks.ts +287 -0
  700. package/utils/plugins/loadPluginOutputStyles.ts +178 -0
  701. package/utils/plugins/lspPluginIntegration.ts +387 -0
  702. package/utils/plugins/lspRecommendation.ts +374 -0
  703. package/utils/plugins/managedPlugins.ts +27 -0
  704. package/utils/plugins/marketplaceHelpers.ts +592 -0
  705. package/utils/plugins/marketplaceManager.ts +2643 -0
  706. package/utils/plugins/mcpPluginIntegration.ts +634 -0
  707. package/utils/plugins/mcpbHandler.ts +968 -0
  708. package/utils/plugins/officialMarketplace.ts +25 -0
  709. package/utils/plugins/officialMarketplaceGcs.ts +216 -0
  710. package/utils/plugins/officialMarketplaceStartupCheck.ts +439 -0
  711. package/utils/plugins/orphanedPluginFilter.ts +114 -0
  712. package/utils/plugins/parseMarketplaceInput.ts +162 -0
  713. package/utils/plugins/performStartupChecks.tsx +70 -0
  714. package/utils/plugins/pluginAutoupdate.ts +284 -0
  715. package/utils/plugins/pluginBlocklist.ts +127 -0
  716. package/utils/plugins/pluginDirectories.ts +178 -0
  717. package/utils/plugins/pluginFlagging.ts +208 -0
  718. package/utils/plugins/pluginIdentifier.ts +123 -0
  719. package/utils/plugins/pluginInstallationHelpers.ts +595 -0
  720. package/utils/plugins/pluginLoader.ts +3302 -0
  721. package/utils/plugins/pluginOptionsStorage.ts +400 -0
  722. package/utils/plugins/pluginPolicy.ts +20 -0
  723. package/utils/plugins/pluginStartupCheck.ts +341 -0
  724. package/utils/plugins/pluginVersioning.ts +157 -0
  725. package/utils/plugins/reconciler.ts +265 -0
  726. package/utils/plugins/refresh.ts +215 -0
  727. package/utils/plugins/schemas.ts +1681 -0
  728. package/utils/plugins/validatePlugin.ts +903 -0
  729. package/utils/plugins/walkPluginMarkdown.ts +69 -0
  730. package/utils/plugins/zipCache.ts +406 -0
  731. package/utils/plugins/zipCacheAdapters.ts +164 -0
  732. package/utils/powershell/dangerousCmdlets.ts +185 -0
  733. package/utils/powershell/parser.ts +1804 -0
  734. package/utils/powershell/staticPrefix.ts +316 -0
  735. package/utils/preflightChecks.tsx +151 -0
  736. package/utils/privacyLevel.ts +55 -0
  737. package/utils/process.ts +68 -0
  738. package/utils/processUserInput/processBashCommand.tsx +140 -0
  739. package/utils/processUserInput/processSlashCommand.tsx +922 -0
  740. package/utils/processUserInput/processTextPrompt.ts +100 -0
  741. package/utils/processUserInput/processUserInput.ts +605 -0
  742. package/utils/profilerBase.ts +46 -0
  743. package/utils/promptCategory.ts +49 -0
  744. package/utils/promptEditor.ts +188 -0
  745. package/utils/promptShellExecution.ts +183 -0
  746. package/utils/proxy.ts +426 -0
  747. package/utils/queryContext.ts +179 -0
  748. package/utils/queryHelpers.ts +552 -0
  749. package/utils/queryProfiler.ts +301 -0
  750. package/utils/queueProcessor.ts +95 -0
  751. package/utils/readEditContext.ts +227 -0
  752. package/utils/readFileInRange.ts +383 -0
  753. package/utils/releaseNotes.ts +360 -0
  754. package/utils/renderOptions.ts +113 -0
  755. package/utils/ripgrep.ts +679 -0
  756. package/utils/sandbox/sandbox-adapter.ts +985 -0
  757. package/utils/sandbox/sandbox-ui-utils.ts +12 -0
  758. package/utils/sanitization.ts +91 -0
  759. package/utils/screenshotClipboard.ts +121 -0
  760. package/utils/sdkEventQueue.ts +134 -0
  761. package/utils/secureStorage/fallbackStorage.ts +70 -0
  762. package/utils/secureStorage/index.ts +17 -0
  763. package/utils/secureStorage/keychainPrefetch.ts +116 -0
  764. package/utils/secureStorage/macOsKeychainHelpers.ts +111 -0
  765. package/utils/secureStorage/macOsKeychainStorage.ts +231 -0
  766. package/utils/secureStorage/plainTextStorage.ts +84 -0
  767. package/utils/semanticBoolean.ts +29 -0
  768. package/utils/semanticNumber.ts +36 -0
  769. package/utils/semver.ts +59 -0
  770. package/utils/sequential.ts +56 -0
  771. package/utils/sessionActivity.ts +133 -0
  772. package/utils/sessionEnvVars.ts +22 -0
  773. package/utils/sessionEnvironment.ts +166 -0
  774. package/utils/sessionFileAccessHooks.ts +250 -0
  775. package/utils/sessionIngressAuth.ts +140 -0
  776. package/utils/sessionRestore.ts +551 -0
  777. package/utils/sessionStart.ts +232 -0
  778. package/utils/sessionState.ts +150 -0
  779. package/utils/sessionStorage.ts +5105 -0
  780. package/utils/sessionStoragePortable.ts +793 -0
  781. package/utils/sessionTitle.ts +129 -0
  782. package/utils/sessionUrl.ts +64 -0
  783. package/utils/set.ts +53 -0
  784. package/utils/settings/allErrors.ts +32 -0
  785. package/utils/settings/applySettingsChange.ts +92 -0
  786. package/utils/settings/changeDetector.ts +488 -0
  787. package/utils/settings/constants.ts +202 -0
  788. package/utils/settings/internalWrites.ts +37 -0
  789. package/utils/settings/managedPath.ts +34 -0
  790. package/utils/settings/mdm/constants.ts +81 -0
  791. package/utils/settings/mdm/rawRead.ts +130 -0
  792. package/utils/settings/mdm/settings.ts +316 -0
  793. package/utils/settings/permissionValidation.ts +262 -0
  794. package/utils/settings/pluginOnlyPolicy.ts +60 -0
  795. package/utils/settings/schemaOutput.ts +8 -0
  796. package/utils/settings/settings.ts +1015 -0
  797. package/utils/settings/settingsCache.ts +80 -0
  798. package/utils/settings/toolValidationConfig.ts +103 -0
  799. package/utils/settings/types.ts +1149 -0
  800. package/utils/settings/validateEditTool.ts +45 -0
  801. package/utils/settings/validation.ts +265 -0
  802. package/utils/settings/validationTips.ts +164 -0
  803. package/utils/shell/bashProvider.ts +255 -0
  804. package/utils/shell/outputLimits.ts +14 -0
  805. package/utils/shell/powershellDetection.ts +107 -0
  806. package/utils/shell/powershellProvider.ts +123 -0
  807. package/utils/shell/prefix.ts +367 -0
  808. package/utils/shell/readOnlyCommandValidation.ts +1893 -0
  809. package/utils/shell/resolveDefaultShell.ts +14 -0
  810. package/utils/shell/shellProvider.ts +33 -0
  811. package/utils/shell/shellToolUtils.ts +22 -0
  812. package/utils/shell/specPrefix.ts +241 -0
  813. package/utils/shellConfig.ts +167 -0
  814. package/utils/sideQuery.ts +222 -0
  815. package/utils/sideQuestion.ts +155 -0
  816. package/utils/signal.ts +43 -0
  817. package/utils/sinks.ts +16 -0
  818. package/utils/skills/skillChangeDetector.ts +311 -0
  819. package/utils/slashCommandParsing.ts +60 -0
  820. package/utils/sleep.ts +84 -0
  821. package/utils/sliceAnsi.ts +91 -0
  822. package/utils/slowOperations.ts +286 -0
  823. package/utils/standaloneAgent.ts +23 -0
  824. package/utils/startupProfiler.ts +194 -0
  825. package/utils/staticRender.tsx +116 -0
  826. package/utils/stats.ts +1061 -0
  827. package/utils/statsCache.ts +434 -0
  828. package/utils/status.tsx +362 -0
  829. package/utils/statusNoticeDefinitions.tsx +198 -0
  830. package/utils/statusNoticeHelpers.ts +20 -0
  831. package/utils/stream.ts +76 -0
  832. package/utils/streamJsonStdoutGuard.ts +123 -0
  833. package/utils/streamlinedTransform.ts +201 -0
  834. package/utils/stringUtils.ts +235 -0
  835. package/utils/subprocessEnv.ts +99 -0
  836. package/utils/suggestions/commandSuggestions.ts +567 -0
  837. package/utils/suggestions/directoryCompletion.ts +263 -0
  838. package/utils/suggestions/shellHistoryCompletion.ts +119 -0
  839. package/utils/suggestions/skillUsageTracking.ts +55 -0
  840. package/utils/suggestions/slackChannelSuggestions.ts +209 -0
  841. package/utils/swarm/It2SetupPrompt.tsx +380 -0
  842. package/utils/swarm/backends/ITermBackend.ts +370 -0
  843. package/utils/swarm/backends/InProcessBackend.ts +339 -0
  844. package/utils/swarm/backends/PaneBackendExecutor.ts +354 -0
  845. package/utils/swarm/backends/TmuxBackend.ts +764 -0
  846. package/utils/swarm/backends/detection.ts +128 -0
  847. package/utils/swarm/backends/it2Setup.ts +245 -0
  848. package/utils/swarm/backends/registry.ts +464 -0
  849. package/utils/swarm/backends/teammateModeSnapshot.ts +87 -0
  850. package/utils/swarm/backends/types.ts +311 -0
  851. package/utils/swarm/constants.ts +33 -0
  852. package/utils/swarm/inProcessRunner.ts +1552 -0
  853. package/utils/swarm/leaderPermissionBridge.ts +54 -0
  854. package/utils/swarm/permissionSync.ts +928 -0
  855. package/utils/swarm/reconnection.ts +119 -0
  856. package/utils/swarm/spawnInProcess.ts +328 -0
  857. package/utils/swarm/spawnUtils.ts +146 -0
  858. package/utils/swarm/teamHelpers.ts +683 -0
  859. package/utils/swarm/teammateInit.ts +129 -0
  860. package/utils/swarm/teammateLayoutManager.ts +107 -0
  861. package/utils/swarm/teammateModel.ts +10 -0
  862. package/utils/swarm/teammatePromptAddendum.ts +18 -0
  863. package/utils/systemDirectories.ts +74 -0
  864. package/utils/systemPrompt.ts +123 -0
  865. package/utils/systemPromptType.ts +14 -0
  866. package/utils/systemTheme.ts +119 -0
  867. package/utils/taggedId.ts +54 -0
  868. package/utils/task/TaskOutput.ts +390 -0
  869. package/utils/task/diskOutput.ts +451 -0
  870. package/utils/task/framework.ts +308 -0
  871. package/utils/task/outputFormatting.ts +38 -0
  872. package/utils/task/sdkProgress.ts +36 -0
  873. package/utils/tasks.ts +862 -0
  874. package/utils/teamDiscovery.ts +81 -0
  875. package/utils/teamMemoryOps.ts +88 -0
  876. package/utils/teammate.ts +292 -0
  877. package/utils/teammateContext.ts +96 -0
  878. package/utils/teammateMailbox.ts +1183 -0
  879. package/utils/telemetry/betaSessionTracing.ts +491 -0
  880. package/utils/telemetry/bigqueryExporter.ts +252 -0
  881. package/utils/telemetry/events.ts +75 -0
  882. package/utils/telemetry/instrumentation.ts +825 -0
  883. package/utils/telemetry/logger.ts +26 -0
  884. package/utils/telemetry/perfettoTracing.ts +1120 -0
  885. package/utils/telemetry/pluginTelemetry.ts +289 -0
  886. package/utils/telemetry/sessionTracing.ts +927 -0
  887. package/utils/telemetry/skillLoadedEvent.ts +39 -0
  888. package/utils/telemetryAttributes.ts +71 -0
  889. package/utils/teleport/api.ts +466 -0
  890. package/utils/teleport/environmentSelection.ts +77 -0
  891. package/utils/teleport/environments.ts +120 -0
  892. package/utils/teleport/gitBundle.ts +292 -0
  893. package/utils/teleport.tsx +1226 -0
  894. package/utils/tempfile.ts +31 -0
  895. package/utils/terminal.ts +131 -0
  896. package/utils/terminalPanel.ts +191 -0
  897. package/utils/textHighlighting.ts +166 -0
  898. package/utils/theme.ts +639 -0
  899. package/utils/thinking.ts +162 -0
  900. package/utils/timeouts.ts +39 -0
  901. package/utils/tmuxSocket.ts +427 -0
  902. package/utils/todo/types.ts +18 -0
  903. package/utils/tokenBudget.ts +73 -0
  904. package/utils/tokens.ts +261 -0
  905. package/utils/toolErrors.ts +132 -0
  906. package/utils/toolPool.ts +79 -0
  907. package/utils/toolResultStorage.ts +1040 -0
  908. package/utils/toolSchemaCache.ts +26 -0
  909. package/utils/toolSearch.ts +756 -0
  910. package/utils/transcriptSearch.ts +202 -0
  911. package/utils/treeify.ts +170 -0
  912. package/utils/truncate.ts +179 -0
  913. package/utils/ultraplan/ccrSession.ts +349 -0
  914. package/utils/ultraplan/keyword.ts +127 -0
  915. package/utils/ultraplan/prompt.txt +1 -0
  916. package/utils/unaryLogging.ts +39 -0
  917. package/utils/undercover.ts +89 -0
  918. package/utils/user.ts +194 -0
  919. package/utils/userAgent.ts +10 -0
  920. package/utils/userPromptKeywords.ts +27 -0
  921. package/utils/uuid.ts +27 -0
  922. package/utils/warningHandler.ts +121 -0
  923. package/utils/which.ts +82 -0
  924. package/utils/windowsPaths.ts +173 -0
  925. package/utils/withResolvers.ts +13 -0
  926. package/utils/words.ts +800 -0
  927. package/utils/workloadContext.ts +57 -0
  928. package/utils/worktree.ts +1519 -0
  929. package/utils/worktreeModeEnabled.ts +11 -0
  930. package/utils/xdg.ts +65 -0
  931. package/utils/xmem.ts +6 -0
  932. package/utils/xml.ts +16 -0
  933. package/utils/yaml.ts +15 -0
  934. package/utils/zodToJsonSchema.ts +23 -0
@@ -0,0 +1,1183 @@
1
+ import type { Base64ImageSource } from '@anthropic-ai/sdk/resources/index.mjs'
2
+ import { readdir, readFile as readFileAsync } from 'fs/promises'
3
+ import * as path from 'path'
4
+ import { posix, win32 } from 'path'
5
+ import { z } from 'zod/v4'
6
+ import {
7
+ PDF_AT_MENTION_INLINE_THRESHOLD,
8
+ PDF_EXTRACT_SIZE_THRESHOLD,
9
+ PDF_MAX_PAGES_PER_READ,
10
+ } from '../../constants/apiLimits.js'
11
+ import { hasBinaryExtension } from '../../constants/files.js'
12
+ import { memoryFreshnessNote } from '../../memdir/memoryAge.js'
13
+ import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'
14
+ import { logEvent } from '../../services/analytics/index.js'
15
+ import {
16
+ type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
17
+ getFileExtensionForAnalytics,
18
+ } from '../../services/analytics/metadata.js'
19
+ import {
20
+ countTokensWithAPI,
21
+ roughTokenCountEstimationForFileType,
22
+ } from '../../services/tokenEstimation.js'
23
+ import {
24
+ activateConditionalSkillsForPaths,
25
+ addSkillDirectories,
26
+ discoverSkillDirsForPaths,
27
+ } from '../../skills/loadSkillsDir.js'
28
+ import type { ToolUseContext } from '../../Tool.js'
29
+ import { buildTool, type ToolDef } from '../../Tool.js'
30
+ import { getCwd } from '../../utils/cwd.js'
31
+ import { getClaudeConfigHomeDir, isEnvTruthy } from '../../utils/envUtils.js'
32
+ import { getErrnoCode, isENOENT } from '../../utils/errors.js'
33
+ import {
34
+ addLineNumbers,
35
+ FILE_NOT_FOUND_CWD_NOTE,
36
+ findSimilarFile,
37
+ getFileModificationTimeAsync,
38
+ suggestPathUnderCwd,
39
+ } from '../../utils/file.js'
40
+ import { logFileOperation } from '../../utils/fileOperationAnalytics.js'
41
+ import { formatFileSize } from '../../utils/format.js'
42
+ import { getFsImplementation } from '../../utils/fsOperations.js'
43
+ import {
44
+ compressImageBufferWithTokenLimit,
45
+ createImageMetadataText,
46
+ detectImageFormatFromBuffer,
47
+ type ImageDimensions,
48
+ ImageResizeError,
49
+ maybeResizeAndDownsampleImageBuffer,
50
+ } from '../../utils/imageResizer.js'
51
+ import { lazySchema } from '../../utils/lazySchema.js'
52
+ import { logError } from '../../utils/log.js'
53
+ import { isAutoMemFile } from '../../utils/memoryFileDetection.js'
54
+ import { createUserMessage } from '../../utils/messages.js'
55
+ import { getCanonicalName, getMainLoopModel } from '../../utils/model/model.js'
56
+ import {
57
+ mapNotebookCellsToToolResult,
58
+ readNotebook,
59
+ } from '../../utils/notebook.js'
60
+ import { expandPath } from '../../utils/path.js'
61
+ import { extractPDFPages, getPDFPageCount, readPDF } from '../../utils/pdf.js'
62
+ import {
63
+ isPDFExtension,
64
+ isPDFSupported,
65
+ parsePDFPageRange,
66
+ } from '../../utils/pdfUtils.js'
67
+ import {
68
+ checkReadPermissionForTool,
69
+ matchingRuleForInput,
70
+ } from '../../utils/permissions/filesystem.js'
71
+ import type { PermissionDecision } from '../../utils/permissions/PermissionResult.js'
72
+ import { matchWildcardPattern } from '../../utils/permissions/shellRuleMatching.js'
73
+ import { readFileInRange } from '../../utils/readFileInRange.js'
74
+ import { semanticNumber } from '../../utils/semanticNumber.js'
75
+ import { jsonStringify } from '../../utils/slowOperations.js'
76
+ import { BASH_TOOL_NAME } from '../BashTool/toolName.js'
77
+ import { getDefaultFileReadingLimits } from './limits.js'
78
+ import {
79
+ DESCRIPTION,
80
+ FILE_READ_TOOL_NAME,
81
+ FILE_UNCHANGED_STUB,
82
+ LINE_FORMAT_INSTRUCTION,
83
+ OFFSET_INSTRUCTION_DEFAULT,
84
+ OFFSET_INSTRUCTION_TARGETED,
85
+ renderPromptTemplate,
86
+ } from './prompt.js'
87
+ import {
88
+ getToolUseSummary,
89
+ renderToolResultMessage,
90
+ renderToolUseErrorMessage,
91
+ renderToolUseMessage,
92
+ renderToolUseTag,
93
+ userFacingName,
94
+ } from './UI.js'
95
+
96
+ // Device files that would hang the process: infinite output or blocking input.
97
+ // Checked by path only (no I/O). Safe devices like /dev/null are intentionally omitted.
98
+ const BLOCKED_DEVICE_PATHS = new Set([
99
+ // Infinite output — never reach EOF
100
+ '/dev/zero',
101
+ '/dev/random',
102
+ '/dev/urandom',
103
+ '/dev/full',
104
+ // Blocks waiting for input
105
+ '/dev/stdin',
106
+ '/dev/tty',
107
+ '/dev/console',
108
+ // Nonsensical to read
109
+ '/dev/stdout',
110
+ '/dev/stderr',
111
+ // fd aliases for stdin/stdout/stderr
112
+ '/dev/fd/0',
113
+ '/dev/fd/1',
114
+ '/dev/fd/2',
115
+ ])
116
+
117
+ function isBlockedDevicePath(filePath: string): boolean {
118
+ if (BLOCKED_DEVICE_PATHS.has(filePath)) return true
119
+ // /proc/self/fd/0-2 and /proc/<pid>/fd/0-2 are Linux aliases for stdio
120
+ if (
121
+ filePath.startsWith('/proc/') &&
122
+ (filePath.endsWith('/fd/0') ||
123
+ filePath.endsWith('/fd/1') ||
124
+ filePath.endsWith('/fd/2'))
125
+ )
126
+ return true
127
+ return false
128
+ }
129
+
130
+ // Narrow no-break space (U+202F) used by some macOS versions in screenshot filenames
131
+ const THIN_SPACE = String.fromCharCode(8239)
132
+
133
+ /**
134
+ * Resolves macOS screenshot paths that may have different space characters.
135
+ * macOS uses either regular space or thin space (U+202F) before AM/PM in screenshot
136
+ * filenames depending on the macOS version. This function tries the alternate space
137
+ * character if the file doesn't exist with the given path.
138
+ *
139
+ * @param filePath - The normalized file path to resolve
140
+ * @returns The path to the actual file on disk (may differ in space character)
141
+ */
142
+ /**
143
+ * For macOS screenshot paths with AM/PM, the space before AM/PM may be a
144
+ * regular space or a thin space depending on the macOS version. Returns
145
+ * the alternate path to try if the original doesn't exist, or undefined.
146
+ */
147
+ function getAlternateScreenshotPath(filePath: string): string | undefined {
148
+ const filename = path.basename(filePath)
149
+ const amPmPattern = /^(.+)([ \u202F])(AM|PM)(\.png)$/
150
+ const match = filename.match(amPmPattern)
151
+ if (!match) return undefined
152
+
153
+ const currentSpace = match[2]
154
+ const alternateSpace = currentSpace === ' ' ? THIN_SPACE : ' '
155
+ return filePath.replace(
156
+ `${currentSpace}${match[3]}${match[4]}`,
157
+ `${alternateSpace}${match[3]}${match[4]}`,
158
+ )
159
+ }
160
+
161
+ // File read listeners - allows other services to be notified when files are read
162
+ type FileReadListener = (filePath: string, content: string) => void
163
+ const fileReadListeners: FileReadListener[] = []
164
+
165
+ export function registerFileReadListener(
166
+ listener: FileReadListener,
167
+ ): () => void {
168
+ fileReadListeners.push(listener)
169
+ return () => {
170
+ const i = fileReadListeners.indexOf(listener)
171
+ if (i >= 0) fileReadListeners.splice(i, 1)
172
+ }
173
+ }
174
+
175
+ export class MaxFileReadTokenExceededError extends Error {
176
+ constructor(
177
+ public tokenCount: number,
178
+ public maxTokens: number,
179
+ ) {
180
+ super(
181
+ `File content (${tokenCount} tokens) exceeds maximum allowed tokens (${maxTokens}). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whole file.`,
182
+ )
183
+ this.name = 'MaxFileReadTokenExceededError'
184
+ }
185
+ }
186
+
187
+ // Common image extensions
188
+ const IMAGE_EXTENSIONS = new Set(['png', 'jpg', 'jpeg', 'gif', 'webp'])
189
+
190
+ /**
191
+ * Detects if a file path is a session-related file for analytics logging.
192
+ * Only matches files within the Claude config directory (e.g., ~/.claude).
193
+ * Returns the type of session file or null if not a session file.
194
+ */
195
+ function detectSessionFileType(
196
+ filePath: string,
197
+ ): 'session_memory' | 'session_transcript' | null {
198
+ const configDir = getClaudeConfigHomeDir()
199
+
200
+ // Only match files within the Claude config directory
201
+ if (!filePath.startsWith(configDir)) {
202
+ return null
203
+ }
204
+
205
+ // Normalize path to use forward slashes for consistent matching across platforms
206
+ const normalizedPath = filePath.split(win32.sep).join(posix.sep)
207
+
208
+ // Session memory files: ~/.claude/session-memory/*.md (including summary.md)
209
+ if (
210
+ normalizedPath.includes('/session-memory/') &&
211
+ normalizedPath.endsWith('.md')
212
+ ) {
213
+ return 'session_memory'
214
+ }
215
+
216
+ // Session JSONL transcript files: ~/.claude/projects/*/*.jsonl
217
+ if (
218
+ normalizedPath.includes('/projects/') &&
219
+ normalizedPath.endsWith('.jsonl')
220
+ ) {
221
+ return 'session_transcript'
222
+ }
223
+
224
+ return null
225
+ }
226
+
227
+ const inputSchema = lazySchema(() =>
228
+ z.strictObject({
229
+ file_path: z.string().describe('The absolute path to the file to read'),
230
+ offset: semanticNumber(z.number().int().nonnegative().optional()).describe(
231
+ 'The line number to start reading from. Only provide if the file is too large to read at once',
232
+ ),
233
+ limit: semanticNumber(z.number().int().positive().optional()).describe(
234
+ 'The number of lines to read. Only provide if the file is too large to read at once.',
235
+ ),
236
+ pages: z
237
+ .string()
238
+ .optional()
239
+ .describe(
240
+ `Page range for PDF files (e.g., "1-5", "3", "10-20"). Only applicable to PDF files. Maximum ${PDF_MAX_PAGES_PER_READ} pages per request.`,
241
+ ),
242
+ }),
243
+ )
244
+ type InputSchema = ReturnType<typeof inputSchema>
245
+
246
+ export type Input = z.infer<InputSchema>
247
+
248
+ const outputSchema = lazySchema(() => {
249
+ // Define the media types supported for images
250
+ const imageMediaTypes = z.enum([
251
+ 'image/jpeg',
252
+ 'image/png',
253
+ 'image/gif',
254
+ 'image/webp',
255
+ ])
256
+
257
+ return z.discriminatedUnion('type', [
258
+ z.object({
259
+ type: z.literal('text'),
260
+ file: z.object({
261
+ filePath: z.string().describe('The path to the file that was read'),
262
+ content: z.string().describe('The content of the file'),
263
+ numLines: z
264
+ .number()
265
+ .describe('Number of lines in the returned content'),
266
+ startLine: z.number().describe('The starting line number'),
267
+ totalLines: z.number().describe('Total number of lines in the file'),
268
+ }),
269
+ }),
270
+ z.object({
271
+ type: z.literal('image'),
272
+ file: z.object({
273
+ base64: z.string().describe('Base64-encoded image data'),
274
+ type: imageMediaTypes.describe('The MIME type of the image'),
275
+ originalSize: z.number().describe('Original file size in bytes'),
276
+ dimensions: z
277
+ .object({
278
+ originalWidth: z
279
+ .number()
280
+ .optional()
281
+ .describe('Original image width in pixels'),
282
+ originalHeight: z
283
+ .number()
284
+ .optional()
285
+ .describe('Original image height in pixels'),
286
+ displayWidth: z
287
+ .number()
288
+ .optional()
289
+ .describe('Displayed image width in pixels (after resizing)'),
290
+ displayHeight: z
291
+ .number()
292
+ .optional()
293
+ .describe('Displayed image height in pixels (after resizing)'),
294
+ })
295
+ .optional()
296
+ .describe('Image dimension info for coordinate mapping'),
297
+ }),
298
+ }),
299
+ z.object({
300
+ type: z.literal('notebook'),
301
+ file: z.object({
302
+ filePath: z.string().describe('The path to the notebook file'),
303
+ cells: z.array(z.any()).describe('Array of notebook cells'),
304
+ }),
305
+ }),
306
+ z.object({
307
+ type: z.literal('pdf'),
308
+ file: z.object({
309
+ filePath: z.string().describe('The path to the PDF file'),
310
+ base64: z.string().describe('Base64-encoded PDF data'),
311
+ originalSize: z.number().describe('Original file size in bytes'),
312
+ }),
313
+ }),
314
+ z.object({
315
+ type: z.literal('parts'),
316
+ file: z.object({
317
+ filePath: z.string().describe('The path to the PDF file'),
318
+ originalSize: z.number().describe('Original file size in bytes'),
319
+ count: z.number().describe('Number of pages extracted'),
320
+ outputDir: z
321
+ .string()
322
+ .describe('Directory containing extracted page images'),
323
+ }),
324
+ }),
325
+ z.object({
326
+ type: z.literal('file_unchanged'),
327
+ file: z.object({
328
+ filePath: z.string().describe('The path to the file'),
329
+ }),
330
+ }),
331
+ ])
332
+ })
333
+ type OutputSchema = ReturnType<typeof outputSchema>
334
+
335
+ export type Output = z.infer<OutputSchema>
336
+
337
+ export const FileReadTool = buildTool({
338
+ name: FILE_READ_TOOL_NAME,
339
+ searchHint: 'read files, images, PDFs, notebooks',
340
+ // Output is bounded by maxTokens (validateContentTokens). Persisting to a
341
+ // file the model reads back with Read is circular — never persist.
342
+ maxResultSizeChars: Infinity,
343
+ strict: true,
344
+ async description() {
345
+ return DESCRIPTION
346
+ },
347
+ async prompt() {
348
+ const limits = getDefaultFileReadingLimits()
349
+ const maxSizeInstruction = limits.includeMaxSizeInPrompt
350
+ ? `. Files larger than ${formatFileSize(limits.maxSizeBytes)} will return an error; use offset and limit for larger files`
351
+ : ''
352
+ const offsetInstruction = limits.targetedRangeNudge
353
+ ? OFFSET_INSTRUCTION_TARGETED
354
+ : OFFSET_INSTRUCTION_DEFAULT
355
+ return renderPromptTemplate(
356
+ pickLineFormatInstruction(),
357
+ maxSizeInstruction,
358
+ offsetInstruction,
359
+ )
360
+ },
361
+ get inputSchema(): InputSchema {
362
+ return inputSchema()
363
+ },
364
+ get outputSchema(): OutputSchema {
365
+ return outputSchema()
366
+ },
367
+ userFacingName,
368
+ getToolUseSummary,
369
+ getActivityDescription(input) {
370
+ const summary = getToolUseSummary(input)
371
+ return summary ? `Reading ${summary}` : 'Reading file'
372
+ },
373
+ isConcurrencySafe() {
374
+ return true
375
+ },
376
+ isReadOnly() {
377
+ return true
378
+ },
379
+ toAutoClassifierInput(input) {
380
+ return input.file_path
381
+ },
382
+ isSearchOrReadCommand() {
383
+ return { isSearch: false, isRead: true }
384
+ },
385
+ getPath({ file_path }): string {
386
+ return file_path || getCwd()
387
+ },
388
+ backfillObservableInput(input) {
389
+ // hooks.mdx documents file_path as absolute; expand so hook allowlists
390
+ // can't be bypassed via ~ or relative paths.
391
+ if (typeof input.file_path === 'string') {
392
+ input.file_path = expandPath(input.file_path)
393
+ }
394
+ },
395
+ async preparePermissionMatcher({ file_path }) {
396
+ return pattern => matchWildcardPattern(pattern, file_path)
397
+ },
398
+ async checkPermissions(input, context): Promise<PermissionDecision> {
399
+ const appState = context.getAppState()
400
+ return checkReadPermissionForTool(
401
+ FileReadTool,
402
+ input,
403
+ appState.toolPermissionContext,
404
+ )
405
+ },
406
+ renderToolUseMessage,
407
+ renderToolUseTag,
408
+ renderToolResultMessage,
409
+ // UI.tsx:140 — ALL types render summary chrome only: "Read N lines",
410
+ // "Read image (42KB)". Never the content itself. The model-facing
411
+ // serialization (below) sends content + CYBER_RISK_MITIGATION_REMINDER
412
+ // + line prefixes; UI shows none of it. Nothing to index. Caught by
413
+ // the render-fidelity test when this initially claimed file.content.
414
+ extractSearchText() {
415
+ return ''
416
+ },
417
+ renderToolUseErrorMessage,
418
+ async validateInput({ file_path, pages }, toolUseContext: ToolUseContext) {
419
+ // Validate pages parameter (pure string parsing, no I/O)
420
+ if (pages !== undefined) {
421
+ const parsed = parsePDFPageRange(pages)
422
+ if (!parsed) {
423
+ return {
424
+ result: false,
425
+ message: `Invalid pages parameter: "${pages}". Use formats like "1-5", "3", or "10-20". Pages are 1-indexed.`,
426
+ errorCode: 7,
427
+ }
428
+ }
429
+ const rangeSize =
430
+ parsed.lastPage === Infinity
431
+ ? PDF_MAX_PAGES_PER_READ + 1
432
+ : parsed.lastPage - parsed.firstPage + 1
433
+ if (rangeSize > PDF_MAX_PAGES_PER_READ) {
434
+ return {
435
+ result: false,
436
+ message: `Page range "${pages}" exceeds maximum of ${PDF_MAX_PAGES_PER_READ} pages per request. Please use a smaller range.`,
437
+ errorCode: 8,
438
+ }
439
+ }
440
+ }
441
+
442
+ // Path expansion + deny rule check (no I/O)
443
+ const fullFilePath = expandPath(file_path)
444
+
445
+ const appState = toolUseContext.getAppState()
446
+ const denyRule = matchingRuleForInput(
447
+ fullFilePath,
448
+ appState.toolPermissionContext,
449
+ 'read',
450
+ 'deny',
451
+ )
452
+ if (denyRule !== null) {
453
+ return {
454
+ result: false,
455
+ message:
456
+ 'File is in a directory that is denied by your permission settings.',
457
+ errorCode: 1,
458
+ }
459
+ }
460
+
461
+ // SECURITY: UNC path check (no I/O) — defer filesystem operations
462
+ // until after user grants permission to prevent NTLM credential leaks
463
+ const isUncPath =
464
+ fullFilePath.startsWith('\\\\') || fullFilePath.startsWith('//')
465
+ if (isUncPath) {
466
+ return { result: true }
467
+ }
468
+
469
+ // Binary extension check (string check on extension only, no I/O).
470
+ // PDF, images, and SVG are excluded - this tool renders them natively.
471
+ const ext = path.extname(fullFilePath).toLowerCase()
472
+ if (
473
+ hasBinaryExtension(fullFilePath) &&
474
+ !isPDFExtension(ext) &&
475
+ !IMAGE_EXTENSIONS.has(ext.slice(1))
476
+ ) {
477
+ return {
478
+ result: false,
479
+ message: `This tool cannot read binary files. The file appears to be a binary ${ext} file. Please use appropriate tools for binary file analysis.`,
480
+ errorCode: 4,
481
+ }
482
+ }
483
+
484
+ // Block specific device files that would hang (infinite output or blocking input).
485
+ // This is a path-based check with no I/O — safe special files like /dev/null are allowed.
486
+ if (isBlockedDevicePath(fullFilePath)) {
487
+ return {
488
+ result: false,
489
+ message: `Cannot read '${file_path}': this device file would block or produce infinite output.`,
490
+ errorCode: 9,
491
+ }
492
+ }
493
+
494
+ return { result: true }
495
+ },
496
+ async call(
497
+ { file_path, offset = 1, limit = undefined, pages },
498
+ context,
499
+ _canUseTool?,
500
+ parentMessage?,
501
+ ) {
502
+ const { readFileState, fileReadingLimits } = context
503
+
504
+ const defaults = getDefaultFileReadingLimits()
505
+ const maxSizeBytes =
506
+ fileReadingLimits?.maxSizeBytes ?? defaults.maxSizeBytes
507
+ const maxTokens = fileReadingLimits?.maxTokens ?? defaults.maxTokens
508
+
509
+ // Telemetry: track when callers override default read limits.
510
+ // Only fires on override (low volume) — event count = override frequency.
511
+ if (fileReadingLimits !== undefined) {
512
+ logEvent('tengu_file_read_limits_override', {
513
+ hasMaxTokens: fileReadingLimits.maxTokens !== undefined,
514
+ hasMaxSizeBytes: fileReadingLimits.maxSizeBytes !== undefined,
515
+ })
516
+ }
517
+
518
+ const ext = path.extname(file_path).toLowerCase().slice(1)
519
+ // Use expandPath for consistent path normalization with FileEditTool/FileWriteTool
520
+ // (especially handles whitespace trimming and Windows path separators)
521
+ const fullFilePath = expandPath(file_path)
522
+
523
+ // Dedup: if we've already read this exact range and the file hasn't
524
+ // changed on disk, return a stub instead of re-sending the full content.
525
+ // The earlier Read tool_result is still in context — two full copies
526
+ // waste cache_creation tokens on every subsequent turn. BQ proxy shows
527
+ // ~18% of Read calls are same-file collisions (up to 2.64% of fleet
528
+ // cache_creation). Only applies to text/notebook reads — images/PDFs
529
+ // aren't cached in readFileState so won't match here.
530
+ //
531
+ // Ant soak: 1,734 dedup hits in 2h, no Read error regression.
532
+ // Killswitch pattern: GB can disable if the stub message confuses
533
+ // the model externally.
534
+ // 3P default: killswitch off = dedup enabled. Client-side only — no
535
+ // server support needed, safe for Bedrock/Vertex/Foundry.
536
+ const dedupKillswitch = getFeatureValue_CACHED_MAY_BE_STALE(
537
+ 'tengu_read_dedup_killswitch',
538
+ false,
539
+ )
540
+ const existingState = dedupKillswitch
541
+ ? undefined
542
+ : readFileState.get(fullFilePath)
543
+ // Only dedup entries that came from a prior Read (offset is always set
544
+ // by Read). Edit/Write store offset=undefined — their readFileState
545
+ // entry reflects post-edit mtime, so deduping against it would wrongly
546
+ // point the model at the pre-edit Read content.
547
+ if (
548
+ existingState &&
549
+ !existingState.isPartialView &&
550
+ existingState.offset !== undefined
551
+ ) {
552
+ const rangeMatch =
553
+ existingState.offset === offset && existingState.limit === limit
554
+ if (rangeMatch) {
555
+ try {
556
+ const mtimeMs = await getFileModificationTimeAsync(fullFilePath)
557
+ if (mtimeMs === existingState.timestamp) {
558
+ const analyticsExt = getFileExtensionForAnalytics(fullFilePath)
559
+ logEvent('tengu_file_read_dedup', {
560
+ ...(analyticsExt !== undefined && { ext: analyticsExt }),
561
+ })
562
+ return {
563
+ data: {
564
+ type: 'file_unchanged' as const,
565
+ file: { filePath: file_path },
566
+ },
567
+ }
568
+ }
569
+ } catch {
570
+ // stat failed — fall through to full read
571
+ }
572
+ }
573
+ }
574
+
575
+ // Discover skills from this file's path (fire-and-forget, non-blocking)
576
+ // Skip in simple mode - no skills available
577
+ const cwd = getCwd()
578
+ if (!isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
579
+ const newSkillDirs = await discoverSkillDirsForPaths([fullFilePath], cwd)
580
+ if (newSkillDirs.length > 0) {
581
+ // Store discovered dirs for attachment display
582
+ for (const dir of newSkillDirs) {
583
+ context.dynamicSkillDirTriggers?.add(dir)
584
+ }
585
+ // Don't await - let skill loading happen in the background
586
+ addSkillDirectories(newSkillDirs).catch(() => {})
587
+ }
588
+
589
+ // Activate conditional skills whose path patterns match this file
590
+ activateConditionalSkillsForPaths([fullFilePath], cwd)
591
+ }
592
+
593
+ try {
594
+ return await callInner(
595
+ file_path,
596
+ fullFilePath,
597
+ fullFilePath,
598
+ ext,
599
+ offset,
600
+ limit,
601
+ pages,
602
+ maxSizeBytes,
603
+ maxTokens,
604
+ readFileState,
605
+ context,
606
+ parentMessage?.message.id,
607
+ )
608
+ } catch (error) {
609
+ // Handle file-not-found: suggest similar files
610
+ const code = getErrnoCode(error)
611
+ if (code === 'ENOENT') {
612
+ // macOS screenshots may use a thin space or regular space before
613
+ // AM/PM — try the alternate before giving up.
614
+ const altPath = getAlternateScreenshotPath(fullFilePath)
615
+ if (altPath) {
616
+ try {
617
+ return await callInner(
618
+ file_path,
619
+ fullFilePath,
620
+ altPath,
621
+ ext,
622
+ offset,
623
+ limit,
624
+ pages,
625
+ maxSizeBytes,
626
+ maxTokens,
627
+ readFileState,
628
+ context,
629
+ parentMessage?.message.id,
630
+ )
631
+ } catch (altError) {
632
+ if (!isENOENT(altError)) {
633
+ throw altError
634
+ }
635
+ // Alt path also missing — fall through to friendly error
636
+ }
637
+ }
638
+
639
+ const similarFilename = findSimilarFile(fullFilePath)
640
+ const cwdSuggestion = await suggestPathUnderCwd(fullFilePath)
641
+ let message = `File does not exist. ${FILE_NOT_FOUND_CWD_NOTE} ${getCwd()}.`
642
+ if (cwdSuggestion) {
643
+ message += ` Did you mean ${cwdSuggestion}?`
644
+ } else if (similarFilename) {
645
+ message += ` Did you mean ${similarFilename}?`
646
+ }
647
+ throw new Error(message)
648
+ }
649
+ throw error
650
+ }
651
+ },
652
+ mapToolResultToToolResultBlockParam(data, toolUseID) {
653
+ switch (data.type) {
654
+ case 'image': {
655
+ return {
656
+ tool_use_id: toolUseID,
657
+ type: 'tool_result',
658
+ content: [
659
+ {
660
+ type: 'image',
661
+ source: {
662
+ type: 'base64',
663
+ data: data.file.base64,
664
+ media_type: data.file.type,
665
+ },
666
+ },
667
+ ],
668
+ }
669
+ }
670
+ case 'notebook':
671
+ return mapNotebookCellsToToolResult(data.file.cells, toolUseID)
672
+ case 'pdf':
673
+ // Return PDF metadata only - the actual content is sent as a supplemental DocumentBlockParam
674
+ return {
675
+ tool_use_id: toolUseID,
676
+ type: 'tool_result',
677
+ content: `PDF file read: ${data.file.filePath} (${formatFileSize(data.file.originalSize)})`,
678
+ }
679
+ case 'parts':
680
+ // Extracted page images are read and sent as image blocks in mapToolResultToAPIMessage
681
+ return {
682
+ tool_use_id: toolUseID,
683
+ type: 'tool_result',
684
+ content: `PDF pages extracted: ${data.file.count} page(s) from ${data.file.filePath} (${formatFileSize(data.file.originalSize)})`,
685
+ }
686
+ case 'file_unchanged':
687
+ return {
688
+ tool_use_id: toolUseID,
689
+ type: 'tool_result',
690
+ content: FILE_UNCHANGED_STUB,
691
+ }
692
+ case 'text': {
693
+ let content: string
694
+
695
+ if (data.file.content) {
696
+ content =
697
+ memoryFileFreshnessPrefix(data) +
698
+ formatFileLines(data.file) +
699
+ (shouldIncludeFileReadMitigation()
700
+ ? CYBER_RISK_MITIGATION_REMINDER
701
+ : '')
702
+ } else {
703
+ // Determine the appropriate warning message
704
+ content =
705
+ data.file.totalLines === 0
706
+ ? '<system-reminder>Warning: the file exists but the contents are empty.</system-reminder>'
707
+ : `<system-reminder>Warning: the file exists but is shorter than the provided offset (${data.file.startLine}). The file has ${data.file.totalLines} lines.</system-reminder>`
708
+ }
709
+
710
+ return {
711
+ tool_use_id: toolUseID,
712
+ type: 'tool_result',
713
+ content,
714
+ }
715
+ }
716
+ }
717
+ },
718
+ } satisfies ToolDef<InputSchema, Output>)
719
+
720
+ function pickLineFormatInstruction(): string {
721
+ return LINE_FORMAT_INSTRUCTION
722
+ }
723
+
724
+ /** Format file content with line numbers. */
725
+ function formatFileLines(file: { content: string; startLine: number }): string {
726
+ return addLineNumbers(file)
727
+ }
728
+
729
+ export const CYBER_RISK_MITIGATION_REMINDER =
730
+ '\n\n<system-reminder>\nWhenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.\n</system-reminder>\n'
731
+
732
+ // Models where cyber risk mitigation should be skipped
733
+ const MITIGATION_EXEMPT_MODELS = new Set(['claude-opus-4-6'])
734
+
735
+ function shouldIncludeFileReadMitigation(): boolean {
736
+ const shortName = getCanonicalName(getMainLoopModel())
737
+ return !MITIGATION_EXEMPT_MODELS.has(shortName)
738
+ }
739
+
740
+ /**
741
+ * Side-channel from call() to mapToolResultToToolResultBlockParam: mtime
742
+ * of auto-memory files, keyed by the `data` object identity. Avoids
743
+ * adding a presentation-only field to the output schema (which flows
744
+ * into SDK types) and avoids sync fs in the mapper. WeakMap auto-GCs
745
+ * when the data object becomes unreachable after rendering.
746
+ */
747
+ const memoryFileMtimes = new WeakMap<object, number>()
748
+
749
+ function memoryFileFreshnessPrefix(data: object): string {
750
+ const mtimeMs = memoryFileMtimes.get(data)
751
+ if (mtimeMs === undefined) return ''
752
+ return memoryFreshnessNote(mtimeMs)
753
+ }
754
+
755
+ async function validateContentTokens(
756
+ content: string,
757
+ ext: string,
758
+ maxTokens?: number,
759
+ ): Promise<void> {
760
+ const effectiveMaxTokens =
761
+ maxTokens ?? getDefaultFileReadingLimits().maxTokens
762
+
763
+ const tokenEstimate = roughTokenCountEstimationForFileType(content, ext)
764
+ if (!tokenEstimate || tokenEstimate <= effectiveMaxTokens / 4) return
765
+
766
+ const tokenCount = await countTokensWithAPI(content)
767
+ const effectiveCount = tokenCount ?? tokenEstimate
768
+
769
+ if (effectiveCount > effectiveMaxTokens) {
770
+ throw new MaxFileReadTokenExceededError(effectiveCount, effectiveMaxTokens)
771
+ }
772
+ }
773
+
774
+ type ImageResult = {
775
+ type: 'image'
776
+ file: {
777
+ base64: string
778
+ type: Base64ImageSource['media_type']
779
+ originalSize: number
780
+ dimensions?: ImageDimensions
781
+ }
782
+ }
783
+
784
+ function createImageResponse(
785
+ buffer: Buffer,
786
+ mediaType: string,
787
+ originalSize: number,
788
+ dimensions?: ImageDimensions,
789
+ ): ImageResult {
790
+ return {
791
+ type: 'image',
792
+ file: {
793
+ base64: buffer.toString('base64'),
794
+ type: `image/${mediaType}` as Base64ImageSource['media_type'],
795
+ originalSize,
796
+ dimensions,
797
+ },
798
+ }
799
+ }
800
+
801
+ /**
802
+ * Inner implementation of call, separated to allow ENOENT handling in the outer call.
803
+ */
804
+ async function callInner(
805
+ file_path: string,
806
+ fullFilePath: string,
807
+ resolvedFilePath: string,
808
+ ext: string,
809
+ offset: number,
810
+ limit: number | undefined,
811
+ pages: string | undefined,
812
+ maxSizeBytes: number,
813
+ maxTokens: number,
814
+ readFileState: ToolUseContext['readFileState'],
815
+ context: ToolUseContext,
816
+ messageId: string | undefined,
817
+ ): Promise<{
818
+ data: Output
819
+ newMessages?: ReturnType<typeof createUserMessage>[]
820
+ }> {
821
+ // --- Notebook ---
822
+ if (ext === 'ipynb') {
823
+ const cells = await readNotebook(resolvedFilePath)
824
+ const cellsJson = jsonStringify(cells)
825
+
826
+ const cellsJsonBytes = Buffer.byteLength(cellsJson)
827
+ if (cellsJsonBytes > maxSizeBytes) {
828
+ throw new Error(
829
+ `Notebook content (${formatFileSize(cellsJsonBytes)}) exceeds maximum allowed size (${formatFileSize(maxSizeBytes)}). ` +
830
+ `Use ${BASH_TOOL_NAME} with jq to read specific portions:\n` +
831
+ ` cat "${file_path}" | jq '.cells[:20]' # First 20 cells\n` +
832
+ ` cat "${file_path}" | jq '.cells[100:120]' # Cells 100-120\n` +
833
+ ` cat "${file_path}" | jq '.cells | length' # Count total cells\n` +
834
+ ` cat "${file_path}" | jq '.cells[] | select(.cell_type=="code") | .source' # All code sources`,
835
+ )
836
+ }
837
+
838
+ await validateContentTokens(cellsJson, ext, maxTokens)
839
+
840
+ // Get mtime via async stat (single call, no prior existence check)
841
+ const stats = await getFsImplementation().stat(resolvedFilePath)
842
+ readFileState.set(fullFilePath, {
843
+ content: cellsJson,
844
+ timestamp: Math.floor(stats.mtimeMs),
845
+ offset,
846
+ limit,
847
+ })
848
+ context.nestedMemoryAttachmentTriggers?.add(fullFilePath)
849
+
850
+ const data = {
851
+ type: 'notebook' as const,
852
+ file: { filePath: file_path, cells },
853
+ }
854
+
855
+ logFileOperation({
856
+ operation: 'read',
857
+ tool: 'FileReadTool',
858
+ filePath: fullFilePath,
859
+ content: cellsJson,
860
+ })
861
+
862
+ return { data }
863
+ }
864
+
865
+ // --- Image (single read, no double-read) ---
866
+ if (IMAGE_EXTENSIONS.has(ext)) {
867
+ // Images have their own size limits (token budget + compression) —
868
+ // don't apply the text maxSizeBytes cap.
869
+ const data = await readImageWithTokenBudget(resolvedFilePath, maxTokens)
870
+ context.nestedMemoryAttachmentTriggers?.add(fullFilePath)
871
+
872
+ logFileOperation({
873
+ operation: 'read',
874
+ tool: 'FileReadTool',
875
+ filePath: fullFilePath,
876
+ content: data.file.base64,
877
+ })
878
+
879
+ const metadataText = data.file.dimensions
880
+ ? createImageMetadataText(data.file.dimensions)
881
+ : null
882
+
883
+ return {
884
+ data,
885
+ ...(metadataText && {
886
+ newMessages: [
887
+ createUserMessage({ content: metadataText, isMeta: true }),
888
+ ],
889
+ }),
890
+ }
891
+ }
892
+
893
+ // --- PDF ---
894
+ if (isPDFExtension(ext)) {
895
+ if (pages) {
896
+ const parsedRange = parsePDFPageRange(pages)
897
+ const extractResult = await extractPDFPages(
898
+ resolvedFilePath,
899
+ parsedRange ?? undefined,
900
+ )
901
+ if (!extractResult.success) {
902
+ throw new Error(extractResult.error.message)
903
+ }
904
+ logEvent('tengu_pdf_page_extraction', {
905
+ success: true,
906
+ pageCount: extractResult.data.file.count,
907
+ fileSize: extractResult.data.file.originalSize,
908
+ hasPageRange: true,
909
+ })
910
+ logFileOperation({
911
+ operation: 'read',
912
+ tool: 'FileReadTool',
913
+ filePath: fullFilePath,
914
+ content: `PDF pages ${pages}`,
915
+ })
916
+ const entries = await readdir(extractResult.data.file.outputDir)
917
+ const imageFiles = entries.filter(f => f.endsWith('.jpg')).sort()
918
+ const imageBlocks = await Promise.all(
919
+ imageFiles.map(async f => {
920
+ const imgPath = path.join(extractResult.data.file.outputDir, f)
921
+ const imgBuffer = await readFileAsync(imgPath)
922
+ const resized = await maybeResizeAndDownsampleImageBuffer(
923
+ imgBuffer,
924
+ imgBuffer.length,
925
+ 'jpeg',
926
+ )
927
+ return {
928
+ type: 'image' as const,
929
+ source: {
930
+ type: 'base64' as const,
931
+ media_type:
932
+ `image/${resized.mediaType}` as Base64ImageSource['media_type'],
933
+ data: resized.buffer.toString('base64'),
934
+ },
935
+ }
936
+ }),
937
+ )
938
+ return {
939
+ data: extractResult.data,
940
+ ...(imageBlocks.length > 0 && {
941
+ newMessages: [
942
+ createUserMessage({ content: imageBlocks, isMeta: true }),
943
+ ],
944
+ }),
945
+ }
946
+ }
947
+
948
+ const pageCount = await getPDFPageCount(resolvedFilePath)
949
+ if (pageCount !== null && pageCount > PDF_AT_MENTION_INLINE_THRESHOLD) {
950
+ throw new Error(
951
+ `This PDF has ${pageCount} pages, which is too many to read at once. ` +
952
+ `Use the pages parameter to read specific page ranges (e.g., pages: "1-5"). ` +
953
+ `Maximum ${PDF_MAX_PAGES_PER_READ} pages per request.`,
954
+ )
955
+ }
956
+
957
+ const fs = getFsImplementation()
958
+ const stats = await fs.stat(resolvedFilePath)
959
+ const shouldExtractPages =
960
+ !isPDFSupported() || stats.size > PDF_EXTRACT_SIZE_THRESHOLD
961
+
962
+ if (shouldExtractPages) {
963
+ const extractResult = await extractPDFPages(resolvedFilePath)
964
+ if (extractResult.success) {
965
+ logEvent('tengu_pdf_page_extraction', {
966
+ success: true,
967
+ pageCount: extractResult.data.file.count,
968
+ fileSize: extractResult.data.file.originalSize,
969
+ })
970
+ } else {
971
+ logEvent('tengu_pdf_page_extraction', {
972
+ success: false,
973
+ available: extractResult.error.reason !== 'unavailable',
974
+ fileSize: stats.size,
975
+ })
976
+ }
977
+ }
978
+
979
+ if (!isPDFSupported()) {
980
+ throw new Error(
981
+ 'Reading full PDFs is not supported with this model. Use a newer model (Sonnet 3.5 v2 or later), ' +
982
+ `or use the pages parameter to read specific page ranges (e.g., pages: "1-5", maximum ${PDF_MAX_PAGES_PER_READ} pages per request). ` +
983
+ 'Page extraction requires poppler-utils: install with `brew install poppler` on macOS or `apt-get install poppler-utils` on Debian/Ubuntu.',
984
+ )
985
+ }
986
+
987
+ const readResult = await readPDF(resolvedFilePath)
988
+ if (!readResult.success) {
989
+ throw new Error(readResult.error.message)
990
+ }
991
+ const pdfData = readResult.data
992
+ logFileOperation({
993
+ operation: 'read',
994
+ tool: 'FileReadTool',
995
+ filePath: fullFilePath,
996
+ content: pdfData.file.base64,
997
+ })
998
+
999
+ return {
1000
+ data: pdfData,
1001
+ newMessages: [
1002
+ createUserMessage({
1003
+ content: [
1004
+ {
1005
+ type: 'document',
1006
+ source: {
1007
+ type: 'base64',
1008
+ media_type: 'application/pdf',
1009
+ data: pdfData.file.base64,
1010
+ },
1011
+ },
1012
+ ],
1013
+ isMeta: true,
1014
+ }),
1015
+ ],
1016
+ }
1017
+ }
1018
+
1019
+ // --- Text file (single async read via readFileInRange) ---
1020
+ const lineOffset = offset === 0 ? 0 : offset - 1
1021
+ const { content, lineCount, totalLines, totalBytes, readBytes, mtimeMs } =
1022
+ await readFileInRange(
1023
+ resolvedFilePath,
1024
+ lineOffset,
1025
+ limit,
1026
+ limit === undefined ? maxSizeBytes : undefined,
1027
+ context.abortController.signal,
1028
+ )
1029
+
1030
+ await validateContentTokens(content, ext, maxTokens)
1031
+
1032
+ readFileState.set(fullFilePath, {
1033
+ content,
1034
+ timestamp: Math.floor(mtimeMs),
1035
+ offset,
1036
+ limit,
1037
+ })
1038
+ context.nestedMemoryAttachmentTriggers?.add(fullFilePath)
1039
+
1040
+ // Snapshot before iterating — a listener that unsubscribes mid-callback
1041
+ // would splice the live array and skip the next listener.
1042
+ for (const listener of fileReadListeners.slice()) {
1043
+ listener(resolvedFilePath, content)
1044
+ }
1045
+
1046
+ const data = {
1047
+ type: 'text' as const,
1048
+ file: {
1049
+ filePath: file_path,
1050
+ content,
1051
+ numLines: lineCount,
1052
+ startLine: offset,
1053
+ totalLines,
1054
+ },
1055
+ }
1056
+ if (isAutoMemFile(fullFilePath)) {
1057
+ memoryFileMtimes.set(data, mtimeMs)
1058
+ }
1059
+
1060
+ logFileOperation({
1061
+ operation: 'read',
1062
+ tool: 'FileReadTool',
1063
+ filePath: fullFilePath,
1064
+ content,
1065
+ })
1066
+
1067
+ const sessionFileType = detectSessionFileType(fullFilePath)
1068
+ const analyticsExt = getFileExtensionForAnalytics(fullFilePath)
1069
+ logEvent('tengu_session_file_read', {
1070
+ totalLines,
1071
+ readLines: lineCount,
1072
+ totalBytes,
1073
+ readBytes,
1074
+ offset,
1075
+ ...(limit !== undefined && { limit }),
1076
+ ...(analyticsExt !== undefined && { ext: analyticsExt }),
1077
+ ...(messageId !== undefined && {
1078
+ messageID:
1079
+ messageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
1080
+ }),
1081
+ is_session_memory: sessionFileType === 'session_memory',
1082
+ is_session_transcript: sessionFileType === 'session_transcript',
1083
+ })
1084
+
1085
+ return { data }
1086
+ }
1087
+
1088
+ /**
1089
+ * Reads an image file and applies token-based compression if needed.
1090
+ * Reads the file ONCE, then applies standard resize. If the result exceeds
1091
+ * the token limit, applies aggressive compression from the same buffer.
1092
+ *
1093
+ * @param filePath - Path to the image file
1094
+ * @param maxTokens - Maximum token budget for the image
1095
+ * @returns Image data with appropriate compression applied
1096
+ */
1097
+ export async function readImageWithTokenBudget(
1098
+ filePath: string,
1099
+ maxTokens: number = getDefaultFileReadingLimits().maxTokens,
1100
+ maxBytes?: number,
1101
+ ): Promise<ImageResult> {
1102
+ // Read file ONCE — capped to maxBytes to avoid OOM on huge files
1103
+ const imageBuffer = await getFsImplementation().readFileBytes(
1104
+ filePath,
1105
+ maxBytes,
1106
+ )
1107
+ const originalSize = imageBuffer.length
1108
+
1109
+ if (originalSize === 0) {
1110
+ throw new Error(`Image file is empty: ${filePath}`)
1111
+ }
1112
+
1113
+ const detectedMediaType = detectImageFormatFromBuffer(imageBuffer)
1114
+ const detectedFormat = detectedMediaType.split('/')[1] || 'png'
1115
+
1116
+ // Try standard resize
1117
+ let result: ImageResult
1118
+ try {
1119
+ const resized = await maybeResizeAndDownsampleImageBuffer(
1120
+ imageBuffer,
1121
+ originalSize,
1122
+ detectedFormat,
1123
+ )
1124
+ result = createImageResponse(
1125
+ resized.buffer,
1126
+ resized.mediaType,
1127
+ originalSize,
1128
+ resized.dimensions,
1129
+ )
1130
+ } catch (e) {
1131
+ if (e instanceof ImageResizeError) throw e
1132
+ logError(e)
1133
+ result = createImageResponse(imageBuffer, detectedFormat, originalSize)
1134
+ }
1135
+
1136
+ // Check if it fits in token budget
1137
+ const estimatedTokens = Math.ceil(result.file.base64.length * 0.125)
1138
+ if (estimatedTokens > maxTokens) {
1139
+ // Aggressive compression from the SAME buffer (no re-read)
1140
+ try {
1141
+ const compressed = await compressImageBufferWithTokenLimit(
1142
+ imageBuffer,
1143
+ maxTokens,
1144
+ detectedMediaType,
1145
+ )
1146
+ return {
1147
+ type: 'image',
1148
+ file: {
1149
+ base64: compressed.base64,
1150
+ type: compressed.mediaType,
1151
+ originalSize,
1152
+ },
1153
+ }
1154
+ } catch (e) {
1155
+ logError(e)
1156
+ // Fallback: heavily compressed version from the SAME buffer
1157
+ try {
1158
+ const sharpModule = await import('sharp')
1159
+ const sharp =
1160
+ (
1161
+ sharpModule as {
1162
+ default?: typeof sharpModule
1163
+ } & typeof sharpModule
1164
+ ).default || sharpModule
1165
+
1166
+ const fallbackBuffer = await sharp(imageBuffer)
1167
+ .resize(400, 400, {
1168
+ fit: 'inside',
1169
+ withoutEnlargement: true,
1170
+ })
1171
+ .jpeg({ quality: 20 })
1172
+ .toBuffer()
1173
+
1174
+ return createImageResponse(fallbackBuffer, 'jpeg', originalSize)
1175
+ } catch (error) {
1176
+ logError(error)
1177
+ return createImageResponse(imageBuffer, detectedFormat, originalSize)
1178
+ }
1179
+ }
1180
+ }
1181
+
1182
+ return result
1183
+ }