@tenex-chat/backend 0.9.1

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 (427) hide show
  1. package/README.md +194 -0
  2. package/dist/backend-wrapper.cjs +3 -0
  3. package/dist/src/index.js +331928 -0
  4. package/package.json +103 -0
  5. package/src/agents/AgentRegistry.ts +418 -0
  6. package/src/agents/AgentStorage.ts +1133 -0
  7. package/src/agents/ConfigResolver.ts +229 -0
  8. package/src/agents/agent-installer.ts +236 -0
  9. package/src/agents/agent-loader.ts +241 -0
  10. package/src/agents/constants.ts +82 -0
  11. package/src/agents/errors.ts +48 -0
  12. package/src/agents/execution/AgentExecutor.ts +561 -0
  13. package/src/agents/execution/ExecutionContextFactory.ts +112 -0
  14. package/src/agents/execution/MessageCompiler.ts +597 -0
  15. package/src/agents/execution/MessageSyncer.ts +100 -0
  16. package/src/agents/execution/PostCompletionChecker.ts +278 -0
  17. package/src/agents/execution/ProgressMonitor.ts +50 -0
  18. package/src/agents/execution/RALResolver.ts +177 -0
  19. package/src/agents/execution/SessionManager.ts +181 -0
  20. package/src/agents/execution/StreamCallbacks.ts +312 -0
  21. package/src/agents/execution/StreamExecutionHandler.ts +579 -0
  22. package/src/agents/execution/StreamSetup.ts +313 -0
  23. package/src/agents/execution/ToolEventHandlers.ts +239 -0
  24. package/src/agents/execution/ToolExecutionTracker.ts +498 -0
  25. package/src/agents/execution/ToolResultUtils.ts +97 -0
  26. package/src/agents/execution/ToolSupervisionWrapper.ts +174 -0
  27. package/src/agents/execution/constants.ts +16 -0
  28. package/src/agents/execution/index.ts +3 -0
  29. package/src/agents/execution/types.ts +96 -0
  30. package/src/agents/execution/utils.ts +26 -0
  31. package/src/agents/index.ts +4 -0
  32. package/src/agents/script-installer.ts +266 -0
  33. package/src/agents/supervision/SupervisorLLMService.ts +253 -0
  34. package/src/agents/supervision/SupervisorOrchestrator.ts +471 -0
  35. package/src/agents/supervision/heuristics/ConsecutiveToolsWithoutTodoHeuristic.ts +73 -0
  36. package/src/agents/supervision/heuristics/DelegationClaimHeuristic.ts +80 -0
  37. package/src/agents/supervision/heuristics/HeuristicRegistry.ts +114 -0
  38. package/src/agents/supervision/heuristics/PendingTodosHeuristic.ts +93 -0
  39. package/src/agents/supervision/heuristics/SilentAgentHeuristic.ts +54 -0
  40. package/src/agents/supervision/heuristics/index.ts +5 -0
  41. package/src/agents/supervision/index.ts +28 -0
  42. package/src/agents/supervision/registerHeuristics.ts +110 -0
  43. package/src/agents/supervision/supervisionHealthCheck.ts +123 -0
  44. package/src/agents/supervision/types.ts +171 -0
  45. package/src/agents/tool-names.ts +46 -0
  46. package/src/agents/tool-normalization.ts +184 -0
  47. package/src/agents/types/index.ts +2 -0
  48. package/src/agents/types/runtime.ts +74 -0
  49. package/src/agents/types/storage.ts +145 -0
  50. package/src/commands/agent/import/index.ts +6 -0
  51. package/src/commands/agent/import/openclaw-distiller.ts +57 -0
  52. package/src/commands/agent/import/openclaw-reader.ts +141 -0
  53. package/src/commands/agent/import/openclaw.ts +154 -0
  54. package/src/commands/agent/index.ts +6 -0
  55. package/src/commands/agent.ts +215 -0
  56. package/src/commands/daemon.ts +198 -0
  57. package/src/commands/doctor.ts +134 -0
  58. package/src/commands/setup/embed.ts +228 -0
  59. package/src/commands/setup/global-system-prompt.ts +223 -0
  60. package/src/commands/setup/image.ts +179 -0
  61. package/src/commands/setup/index.ts +16 -0
  62. package/src/commands/setup/interactive.ts +95 -0
  63. package/src/commands/setup/llm.ts +38 -0
  64. package/src/commands/setup/onboarding.ts +294 -0
  65. package/src/commands/setup/providers.ts +27 -0
  66. package/src/constants.ts +34 -0
  67. package/src/conversations/ConversationDiskReader.ts +148 -0
  68. package/src/conversations/ConversationRegistry.ts +728 -0
  69. package/src/conversations/ConversationStore.ts +868 -0
  70. package/src/conversations/MessageBuilder.ts +866 -0
  71. package/src/conversations/executionTime.ts +62 -0
  72. package/src/conversations/formatters/DelegationXmlFormatter.ts +64 -0
  73. package/src/conversations/formatters/ThreadedConversationFormatter.ts +303 -0
  74. package/src/conversations/formatters/index.ts +9 -0
  75. package/src/conversations/formatters/utils/MessageFormatter.ts +46 -0
  76. package/src/conversations/formatters/utils/TimestampFormatter.ts +56 -0
  77. package/src/conversations/formatters/utils/TreeBuilder.ts +131 -0
  78. package/src/conversations/formatters/utils/TreeRenderer.ts +49 -0
  79. package/src/conversations/index.ts +2 -0
  80. package/src/conversations/persistence/ToolMessageStorage.ts +143 -0
  81. package/src/conversations/search/ConversationIndexManager.ts +393 -0
  82. package/src/conversations/search/QueryParser.ts +114 -0
  83. package/src/conversations/search/SearchEngine.ts +175 -0
  84. package/src/conversations/search/SnippetExtractor.ts +345 -0
  85. package/src/conversations/search/embeddings/ConversationEmbeddingService.ts +484 -0
  86. package/src/conversations/search/embeddings/ConversationIndexingJob.ts +320 -0
  87. package/src/conversations/search/embeddings/IndexingStateManager.ts +338 -0
  88. package/src/conversations/search/embeddings/index.ts +18 -0
  89. package/src/conversations/search/index.ts +49 -0
  90. package/src/conversations/search/types.ts +124 -0
  91. package/src/conversations/services/CategoryManager.ts +160 -0
  92. package/src/conversations/services/ConversationResolver.ts +296 -0
  93. package/src/conversations/services/ConversationSummarizer.ts +234 -0
  94. package/src/conversations/services/MetadataDebounceManager.ts +188 -0
  95. package/src/conversations/services/index.ts +2 -0
  96. package/src/conversations/types.ts +148 -0
  97. package/src/conversations/utils/content-utils.ts +69 -0
  98. package/src/conversations/utils/image-placeholder.ts +281 -0
  99. package/src/conversations/utils/image-url-utils.ts +171 -0
  100. package/src/conversations/utils/multimodal-content.ts +90 -0
  101. package/src/conversations/utils/tool-result-truncator.ts +159 -0
  102. package/src/daemon/Daemon.ts +1883 -0
  103. package/src/daemon/ProjectRuntime.ts +657 -0
  104. package/src/daemon/RestartState.ts +152 -0
  105. package/src/daemon/RuntimeLifecycle.ts +268 -0
  106. package/src/daemon/SubscriptionManager.ts +305 -0
  107. package/src/daemon/UnixSocketTransport.ts +318 -0
  108. package/src/daemon/filters/SubscriptionFilterBuilder.ts +119 -0
  109. package/src/daemon/index.ts +9 -0
  110. package/src/daemon/routing/DaemonRouter.ts +491 -0
  111. package/src/daemon/types.ts +150 -0
  112. package/src/daemon/utils/routing-log.ts +76 -0
  113. package/src/daemon/utils/telemetry.ts +173 -0
  114. package/src/event-handler/agentDeletion.ts +383 -0
  115. package/src/event-handler/index.ts +749 -0
  116. package/src/event-handler/newConversation.ts +165 -0
  117. package/src/event-handler/project.ts +166 -0
  118. package/src/event-handler/reply.ts +18 -0
  119. package/src/events/NDKAgentDefinition.ts +292 -0
  120. package/src/events/NDKAgentLesson.ts +106 -0
  121. package/src/events/NDKEventMetadata.ts +34 -0
  122. package/src/events/NDKMCPTool.ts +60 -0
  123. package/src/events/NDKProjectStatus.ts +384 -0
  124. package/src/events/index.ts +4 -0
  125. package/src/index.ts +126 -0
  126. package/src/lib/agent-home.ts +334 -0
  127. package/src/lib/error-formatter.ts +200 -0
  128. package/src/lib/fs/filesystem.ts +128 -0
  129. package/src/lib/fs/index.ts +1 -0
  130. package/src/lib/json-parser.ts +30 -0
  131. package/src/lib/string.ts +15 -0
  132. package/src/lib/time.ts +74 -0
  133. package/src/llm/ChunkHandler.ts +277 -0
  134. package/src/llm/FinishHandler.ts +250 -0
  135. package/src/llm/LLMConfigEditor.ts +154 -0
  136. package/src/llm/LLMServiceFactory.ts +230 -0
  137. package/src/llm/MessageProcessor.ts +90 -0
  138. package/src/llm/RecordingState.ts +37 -0
  139. package/src/llm/StreamPublisher.ts +40 -0
  140. package/src/llm/TracingUtils.ts +77 -0
  141. package/src/llm/chunk-validators.ts +57 -0
  142. package/src/llm/constants.ts +6 -0
  143. package/src/llm/index.ts +12 -0
  144. package/src/llm/meta/MetaModelResolver.ts +352 -0
  145. package/src/llm/meta/index.ts +11 -0
  146. package/src/llm/middleware/flight-recorder.ts +188 -0
  147. package/src/llm/providers/MockProvider.ts +332 -0
  148. package/src/llm/providers/agent/ClaudeCodeProvider.ts +343 -0
  149. package/src/llm/providers/agent/ClaudeCodeToolsAdapter.ts +203 -0
  150. package/src/llm/providers/agent/CodexAppServerProvider.ts +214 -0
  151. package/src/llm/providers/agent/CodexAppServerToolsAdapter.ts +91 -0
  152. package/src/llm/providers/agent/index.ts +10 -0
  153. package/src/llm/providers/base/AgentProvider.ts +107 -0
  154. package/src/llm/providers/base/BaseProvider.ts +114 -0
  155. package/src/llm/providers/base/StandardProvider.ts +38 -0
  156. package/src/llm/providers/base/index.ts +9 -0
  157. package/src/llm/providers/index.ts +106 -0
  158. package/src/llm/providers/key-manager.ts +238 -0
  159. package/src/llm/providers/ollama-models.ts +105 -0
  160. package/src/llm/providers/openrouter-models.ts +102 -0
  161. package/src/llm/providers/provider-ids.ts +18 -0
  162. package/src/llm/providers/registry/ProviderRegistry.ts +414 -0
  163. package/src/llm/providers/registry/index.ts +7 -0
  164. package/src/llm/providers/standard/AnthropicProvider.ts +71 -0
  165. package/src/llm/providers/standard/OllamaProvider.ts +59 -0
  166. package/src/llm/providers/standard/OpenAIProvider.ts +44 -0
  167. package/src/llm/providers/standard/OpenRouterProvider.ts +103 -0
  168. package/src/llm/providers/standard/index.ts +10 -0
  169. package/src/llm/providers/types.ts +194 -0
  170. package/src/llm/providers/usage-metadata.ts +78 -0
  171. package/src/llm/service.ts +713 -0
  172. package/src/llm/types.ts +167 -0
  173. package/src/llm/utils/ConfigurationManager.ts +650 -0
  174. package/src/llm/utils/ConfigurationTester.ts +229 -0
  175. package/src/llm/utils/ModelSelector.ts +212 -0
  176. package/src/llm/utils/ProviderConfigUI.ts +177 -0
  177. package/src/llm/utils/claudeCodePromptCompiler.ts +141 -0
  178. package/src/llm/utils/codex-models.ts +53 -0
  179. package/src/llm/utils/context-window-cache.ts +30 -0
  180. package/src/llm/utils/models-dev-cache.ts +267 -0
  181. package/src/llm/utils/provider-setup.ts +50 -0
  182. package/src/llm/utils/tool-errors.ts +78 -0
  183. package/src/llm/utils/usage.ts +74 -0
  184. package/src/logging/EventRoutingLogger.ts +205 -0
  185. package/src/nostr/AgentEventDecoder.ts +357 -0
  186. package/src/nostr/AgentEventEncoder.ts +677 -0
  187. package/src/nostr/AgentProfilePublisher.ts +657 -0
  188. package/src/nostr/AgentPublisher.ts +437 -0
  189. package/src/nostr/BlossomService.ts +226 -0
  190. package/src/nostr/InterventionPublisher.ts +132 -0
  191. package/src/nostr/TagExtractor.ts +228 -0
  192. package/src/nostr/collectEvents.ts +83 -0
  193. package/src/nostr/constants.ts +38 -0
  194. package/src/nostr/encryption.ts +26 -0
  195. package/src/nostr/index.ts +31 -0
  196. package/src/nostr/keys.ts +17 -0
  197. package/src/nostr/kinds.ts +37 -0
  198. package/src/nostr/ndkClient.ts +72 -0
  199. package/src/nostr/relays.ts +43 -0
  200. package/src/nostr/trace-context.ts +39 -0
  201. package/src/nostr/types.ts +227 -0
  202. package/src/nostr/utils.ts +84 -0
  203. package/src/prompts/core/FragmentRegistry.ts +30 -0
  204. package/src/prompts/core/PromptBuilder.ts +98 -0
  205. package/src/prompts/core/index.ts +3 -0
  206. package/src/prompts/core/types.ts +13 -0
  207. package/src/prompts/fragments/00-global-system-prompt.ts +44 -0
  208. package/src/prompts/fragments/01-agent-identity.ts +69 -0
  209. package/src/prompts/fragments/02-agent-home-directory.ts +114 -0
  210. package/src/prompts/fragments/03-system-reminders-explanation.ts +14 -0
  211. package/src/prompts/fragments/04-relay-configuration.ts +38 -0
  212. package/src/prompts/fragments/05-delegation-chain.ts +45 -0
  213. package/src/prompts/fragments/06-agent-todos.ts +74 -0
  214. package/src/prompts/fragments/06-todo-usage-guidance.ts +34 -0
  215. package/src/prompts/fragments/07-meta-project-context.ts +234 -0
  216. package/src/prompts/fragments/08-active-conversations.ts +382 -0
  217. package/src/prompts/fragments/09-recent-conversations.ts +153 -0
  218. package/src/prompts/fragments/10-referenced-article.ts +21 -0
  219. package/src/prompts/fragments/11-nudges.ts +134 -0
  220. package/src/prompts/fragments/12-skills.ts +127 -0
  221. package/src/prompts/fragments/13-available-nudges.ts +122 -0
  222. package/src/prompts/fragments/15-available-agents.ts +53 -0
  223. package/src/prompts/fragments/16-stay-in-your-lane.ts +41 -0
  224. package/src/prompts/fragments/17-todo-before-delegation.ts +39 -0
  225. package/src/prompts/fragments/20-voice-mode.ts +62 -0
  226. package/src/prompts/fragments/22-scheduled-tasks.ts +175 -0
  227. package/src/prompts/fragments/24-retrieved-lessons.ts +26 -0
  228. package/src/prompts/fragments/25-rag-instructions.ts +333 -0
  229. package/src/prompts/fragments/26-mcp-resources.ts +237 -0
  230. package/src/prompts/fragments/27-memorized-reports.ts +77 -0
  231. package/src/prompts/fragments/28-agent-directed-monitoring.ts +32 -0
  232. package/src/prompts/fragments/29-rag-collections.ts +50 -0
  233. package/src/prompts/fragments/30-worktree-context.ts +98 -0
  234. package/src/prompts/fragments/31-agents-md-guidance.ts +96 -0
  235. package/src/prompts/fragments/32-process-metrics.ts +72 -0
  236. package/src/prompts/fragments/debug-mode.ts +48 -0
  237. package/src/prompts/fragments/delegation-completion.ts +44 -0
  238. package/src/prompts/fragments/index.ts +91 -0
  239. package/src/prompts/index.ts +21 -0
  240. package/src/prompts/utils/systemPromptBuilder.ts +777 -0
  241. package/src/scripts/migrate-prefix-index.ts +157 -0
  242. package/src/services/AgentDefinitionMonitor.ts +701 -0
  243. package/src/services/ConfigService.ts +723 -0
  244. package/src/services/CooldownRegistry.ts +199 -0
  245. package/src/services/LLMOperationsRegistry.ts +424 -0
  246. package/src/services/OwnerAgentListService.ts +354 -0
  247. package/src/services/PubkeyService.ts +308 -0
  248. package/src/services/agents/AgentMetadataStore.ts +72 -0
  249. package/src/services/agents/AgentResolution.ts +59 -0
  250. package/src/services/agents/EscalationService.ts +281 -0
  251. package/src/services/agents/NDKAgentDiscovery.ts +95 -0
  252. package/src/services/agents/index.ts +7 -0
  253. package/src/services/agents-md/AgentsMdService.ts +184 -0
  254. package/src/services/agents-md/SystemReminderInjector.ts +238 -0
  255. package/src/services/agents-md/index.ts +11 -0
  256. package/src/services/apns/APNsClient.ts +203 -0
  257. package/src/services/apns/APNsService.ts +358 -0
  258. package/src/services/apns/index.ts +11 -0
  259. package/src/services/apns/types.ts +80 -0
  260. package/src/services/compression/CompressionService.ts +445 -0
  261. package/src/services/compression/compression-schema.ts +28 -0
  262. package/src/services/compression/compression-types.ts +74 -0
  263. package/src/services/compression/compression-utils.ts +587 -0
  264. package/src/services/config/types.ts +394 -0
  265. package/src/services/dispatch/AgentDispatchService.ts +937 -0
  266. package/src/services/dispatch/AgentRouter.ts +181 -0
  267. package/src/services/dispatch/DelegationCompletionHandler.ts +232 -0
  268. package/src/services/embedding/EmbeddingProvider.ts +188 -0
  269. package/src/services/embedding/index.ts +5 -0
  270. package/src/services/event-context/EventContextService.ts +108 -0
  271. package/src/services/event-context/index.ts +2 -0
  272. package/src/services/heuristics/ContextBuilder.ts +106 -0
  273. package/src/services/heuristics/HeuristicEngine.ts +200 -0
  274. package/src/services/heuristics/formatters.ts +58 -0
  275. package/src/services/heuristics/index.ts +12 -0
  276. package/src/services/heuristics/rules/index.ts +25 -0
  277. package/src/services/heuristics/rules/todoBeforeDelegation.ts +69 -0
  278. package/src/services/heuristics/rules/todoReminderOnToolUse.ts +63 -0
  279. package/src/services/heuristics/types.ts +144 -0
  280. package/src/services/image/ImageGenerationService.ts +389 -0
  281. package/src/services/image/index.ts +12 -0
  282. package/src/services/intervention/InterventionService.ts +1352 -0
  283. package/src/services/intervention/index.ts +7 -0
  284. package/src/services/mcp/MCPManager.ts +683 -0
  285. package/src/services/mcp/McpNotificationDelivery.ts +139 -0
  286. package/src/services/mcp/McpSubscriptionService.ts +653 -0
  287. package/src/services/mcp/mcpInstaller.ts +130 -0
  288. package/src/services/nip46/Nip46SigningLog.ts +81 -0
  289. package/src/services/nip46/Nip46SigningService.ts +467 -0
  290. package/src/services/nip46/index.ts +4 -0
  291. package/src/services/nudge/NudgeService.ts +224 -0
  292. package/src/services/nudge/NudgeWhitelistService.ts +382 -0
  293. package/src/services/nudge/index.ts +5 -0
  294. package/src/services/nudge/types.ts +83 -0
  295. package/src/services/projects/ProjectContext.ts +672 -0
  296. package/src/services/projects/ProjectContextStore.ts +102 -0
  297. package/src/services/projects/index.ts +6 -0
  298. package/src/services/prompt-compiler/index.ts +15 -0
  299. package/src/services/prompt-compiler/prompt-compiler-service.ts +1143 -0
  300. package/src/services/pubkey-gate/PubkeyGateService.ts +93 -0
  301. package/src/services/pubkey-gate/index.ts +1 -0
  302. package/src/services/rag/EmbeddingProviderFactory.ts +292 -0
  303. package/src/services/rag/LanceDBMaintenanceService.ts +211 -0
  304. package/src/services/rag/RAGDatabaseService.ts +173 -0
  305. package/src/services/rag/RAGOperations.ts +682 -0
  306. package/src/services/rag/RAGService.ts +240 -0
  307. package/src/services/rag/RagSubscriptionService.ts +618 -0
  308. package/src/services/rag/rag-utils.ts +174 -0
  309. package/src/services/ral/PendingDelegationsRegistry.ts +168 -0
  310. package/src/services/ral/RALRegistry.ts +2782 -0
  311. package/src/services/ral/index.ts +4 -0
  312. package/src/services/ral/types.ts +292 -0
  313. package/src/services/reports/LocalReportStore.ts +380 -0
  314. package/src/services/reports/ReportEmbeddingService.ts +430 -0
  315. package/src/services/reports/ReportService.ts +440 -0
  316. package/src/services/reports/articleUtils.ts +52 -0
  317. package/src/services/reports/index.ts +7 -0
  318. package/src/services/scheduling/SchedulerService.ts +1057 -0
  319. package/src/services/scheduling/errors.ts +14 -0
  320. package/src/services/scheduling/index.ts +7 -0
  321. package/src/services/scheduling/utils.ts +77 -0
  322. package/src/services/search/SearchProviderRegistry.ts +78 -0
  323. package/src/services/search/UnifiedSearchService.ts +218 -0
  324. package/src/services/search/index.ts +47 -0
  325. package/src/services/search/projectFilter.ts +22 -0
  326. package/src/services/search/providers/ConversationSearchProvider.ts +48 -0
  327. package/src/services/search/providers/LessonSearchProvider.ts +75 -0
  328. package/src/services/search/providers/ReportSearchProvider.ts +49 -0
  329. package/src/services/search/types.ts +144 -0
  330. package/src/services/skill/SkillService.ts +482 -0
  331. package/src/services/skill/index.ts +2 -0
  332. package/src/services/skill/types.ts +70 -0
  333. package/src/services/status/OperationsStatusService.ts +276 -0
  334. package/src/services/status/ProjectStatusService.ts +522 -0
  335. package/src/services/status/index.ts +11 -0
  336. package/src/services/storage/PrefixKVStore.ts +242 -0
  337. package/src/services/storage/index.ts +1 -0
  338. package/src/services/system-reminder/SystemReminderUtils.ts +96 -0
  339. package/src/services/system-reminder/index.ts +7 -0
  340. package/src/services/trust-pubkeys/TrustPubkeyService.ts +325 -0
  341. package/src/services/trust-pubkeys/index.ts +2 -0
  342. package/src/telemetry/ConversationSpanManager.ts +111 -0
  343. package/src/telemetry/EventLoopMonitor.ts +206 -0
  344. package/src/telemetry/LLMSpanRegistry.ts +20 -0
  345. package/src/telemetry/NostrSpanProcessor.ts +89 -0
  346. package/src/telemetry/ToolCallSpanProcessor.ts +66 -0
  347. package/src/telemetry/diagnostics.ts +27 -0
  348. package/src/telemetry/setup.ts +120 -0
  349. package/src/tools/implementations/agents_discover.ts +121 -0
  350. package/src/tools/implementations/agents_hire.ts +127 -0
  351. package/src/tools/implementations/agents_list.ts +96 -0
  352. package/src/tools/implementations/agents_publish.ts +611 -0
  353. package/src/tools/implementations/agents_read.ts +173 -0
  354. package/src/tools/implementations/agents_write.ts +200 -0
  355. package/src/tools/implementations/ask.ts +411 -0
  356. package/src/tools/implementations/change_model.ts +141 -0
  357. package/src/tools/implementations/conversation_get.ts +661 -0
  358. package/src/tools/implementations/conversation_list.ts +377 -0
  359. package/src/tools/implementations/conversation_search.ts +370 -0
  360. package/src/tools/implementations/delegate.ts +327 -0
  361. package/src/tools/implementations/delegate_crossproject.ts +209 -0
  362. package/src/tools/implementations/delegate_followup.ts +300 -0
  363. package/src/tools/implementations/fs_edit.ts +162 -0
  364. package/src/tools/implementations/fs_glob.ts +182 -0
  365. package/src/tools/implementations/fs_grep.ts +513 -0
  366. package/src/tools/implementations/fs_read.ts +332 -0
  367. package/src/tools/implementations/fs_write.ts +113 -0
  368. package/src/tools/implementations/generate_image.ts +259 -0
  369. package/src/tools/implementations/home_fs.ts +515 -0
  370. package/src/tools/implementations/kill.ts +651 -0
  371. package/src/tools/implementations/learn.ts +166 -0
  372. package/src/tools/implementations/lesson-formatter.ts +38 -0
  373. package/src/tools/implementations/lesson_delete.ts +164 -0
  374. package/src/tools/implementations/lesson_get.ts +105 -0
  375. package/src/tools/implementations/lessons_list.ts +153 -0
  376. package/src/tools/implementations/mcp_resource_read.ts +161 -0
  377. package/src/tools/implementations/mcp_subscribe.ts +158 -0
  378. package/src/tools/implementations/mcp_subscription_stop.ts +85 -0
  379. package/src/tools/implementations/nostr_fetch.ts +149 -0
  380. package/src/tools/implementations/nostr_publish_as_user.ts +353 -0
  381. package/src/tools/implementations/project_list.ts +146 -0
  382. package/src/tools/implementations/rag_add_documents.ts +573 -0
  383. package/src/tools/implementations/rag_create_collection.ts +65 -0
  384. package/src/tools/implementations/rag_delete_collection.ts +68 -0
  385. package/src/tools/implementations/rag_list_collections.ts +77 -0
  386. package/src/tools/implementations/rag_query.ts +107 -0
  387. package/src/tools/implementations/rag_subscription_create.ts +105 -0
  388. package/src/tools/implementations/rag_subscription_delete.ts +80 -0
  389. package/src/tools/implementations/rag_subscription_get.ts +123 -0
  390. package/src/tools/implementations/rag_subscription_list.ts +128 -0
  391. package/src/tools/implementations/report_delete.ts +79 -0
  392. package/src/tools/implementations/report_read.ts +160 -0
  393. package/src/tools/implementations/report_write.ts +278 -0
  394. package/src/tools/implementations/reports_list.ts +77 -0
  395. package/src/tools/implementations/schedule_task.ts +104 -0
  396. package/src/tools/implementations/schedule_task_cancel.ts +62 -0
  397. package/src/tools/implementations/schedule_task_once.ts +128 -0
  398. package/src/tools/implementations/schedule_tasks_list.ts +79 -0
  399. package/src/tools/implementations/search.ts +160 -0
  400. package/src/tools/implementations/shell.ts +553 -0
  401. package/src/tools/implementations/todo.ts +260 -0
  402. package/src/tools/implementations/upload_blob.ts +381 -0
  403. package/src/tools/implementations/web_fetch.ts +153 -0
  404. package/src/tools/implementations/web_search.ts +250 -0
  405. package/src/tools/registry.ts +670 -0
  406. package/src/tools/types.ts +177 -0
  407. package/src/tools/utils.ts +256 -0
  408. package/src/types/event-ids.ts +320 -0
  409. package/src/types/index.ts +46 -0
  410. package/src/utils/agentFetcher.ts +107 -0
  411. package/src/utils/cli-error.ts +29 -0
  412. package/src/utils/conversation-id.ts +27 -0
  413. package/src/utils/conversation-utils.ts +1 -0
  414. package/src/utils/delegation-chain.ts +357 -0
  415. package/src/utils/error-handler.ts +42 -0
  416. package/src/utils/git/gitignore.ts +69 -0
  417. package/src/utils/git/index.ts +2 -0
  418. package/src/utils/git/initializeGitRepo.ts +204 -0
  419. package/src/utils/git/worktree.ts +260 -0
  420. package/src/utils/lessonFormatter.ts +70 -0
  421. package/src/utils/lessonTrust.ts +24 -0
  422. package/src/utils/lockfile.ts +123 -0
  423. package/src/utils/logger.ts +149 -0
  424. package/src/utils/nostr-entity-parser.ts +365 -0
  425. package/src/utils/process.ts +49 -0
  426. package/src/wrapper.ts +262 -0
  427. package/tsconfig.json +41 -0
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Image Placeholder Strategy for Token Cost Reduction
3
+ *
4
+ * Images in tool results accumulate massive token costs (~1,600 tokens per image).
5
+ * This module implements a placeholder strategy:
6
+ *
7
+ * 1. First appearance: Image is shown in full (agent can see/analyze it)
8
+ * 2. Subsequent appearances: Image is replaced with a text placeholder
9
+ * that references fs_read(tool='<eventId>') for retrieval
10
+ *
11
+ * This reduces token costs by 95-99% for image-heavy conversations while
12
+ * maintaining retrieval capability.
13
+ */
14
+
15
+ import type { ToolResultPart } from "ai";
16
+ import { isImageUrl } from "./image-url-utils";
17
+
18
+ /**
19
+ * Prefix used for image placeholders - allows detection of replaced images
20
+ */
21
+ export const IMAGE_PLACEHOLDER_PREFIX = "[Image:";
22
+
23
+ /**
24
+ * Tracker interface for managing which images have been seen in the conversation
25
+ */
26
+ export interface ImageTracker {
27
+ /**
28
+ * Check if an image URL has already been seen in this conversation
29
+ */
30
+ hasBeenSeen(imageUrl: string): boolean;
31
+
32
+ /**
33
+ * Mark an image URL as seen (typically after first display)
34
+ */
35
+ markAsSeen(imageUrl: string): void;
36
+
37
+ /**
38
+ * Get all seen image URLs
39
+ */
40
+ getSeenUrls(): Set<string>;
41
+ }
42
+
43
+ /**
44
+ * Create a new image tracker for a conversation
45
+ */
46
+ export function createImageTracker(): ImageTracker {
47
+ const seenUrls = new Set<string>();
48
+
49
+ return {
50
+ hasBeenSeen(imageUrl: string): boolean {
51
+ return seenUrls.has(imageUrl);
52
+ },
53
+
54
+ markAsSeen(imageUrl: string): void {
55
+ seenUrls.add(imageUrl);
56
+ },
57
+
58
+ getSeenUrls(): Set<string> {
59
+ return seenUrls;
60
+ },
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Create a placeholder text for an image that has been seen before.
66
+ *
67
+ * @param imageUrlOrName - The image URL or filename
68
+ * @param eventId - The event ID for retrieval via fs_read
69
+ * @returns Placeholder text with retrieval instructions
70
+ */
71
+ export function createImagePlaceholder(
72
+ imageUrlOrName: string,
73
+ eventId: string | undefined
74
+ ): string {
75
+ // Extract filename from URL if it looks like a URL
76
+ let filename = imageUrlOrName;
77
+ try {
78
+ const url = new URL(imageUrlOrName);
79
+ const pathParts = url.pathname.split("/");
80
+ const lastPart = pathParts[pathParts.length - 1];
81
+ if (lastPart) {
82
+ filename = lastPart;
83
+ }
84
+ } catch {
85
+ // Not a URL, use as-is
86
+ }
87
+
88
+ if (eventId) {
89
+ return `${IMAGE_PLACEHOLDER_PREFIX} ${filename} - use fs_read(tool="${eventId}") to retrieve]`;
90
+ }
91
+
92
+ // Fallback when eventId is not available (shouldn't happen normally)
93
+ return `${IMAGE_PLACEHOLDER_PREFIX} ${filename} - original context lost, cannot retrieve]`;
94
+ }
95
+
96
+ /**
97
+ * Regex pattern to match HTTP(S) URLs in text.
98
+ * Must match the raw URL as it appears in text, including trailing chars.
99
+ */
100
+ const URL_PATTERN = /https?:\/\/[^\s<>"]+/gi;
101
+
102
+ /**
103
+ * Normalize a raw URL match by removing trailing punctuation and validating.
104
+ * Returns null if the normalized URL is invalid.
105
+ *
106
+ * @param rawUrl - The raw URL as matched from text
107
+ * @returns Object with rawUrl (as matched) and normalizedUrl, or null if invalid
108
+ */
109
+ function normalizeImageUrl(rawUrl: string): { rawUrl: string; normalizedUrl: string } | null {
110
+ // Remove trailing punctuation that might have been captured (., ), ], etc.)
111
+ let cleanUrl = rawUrl.replace(/[).,\]]+$/, "");
112
+
113
+ try {
114
+ // Validate and normalize via URL constructor
115
+ const normalized = new URL(cleanUrl).href;
116
+ if (isImageUrl(normalized)) {
117
+ return { rawUrl: cleanUrl, normalizedUrl: normalized };
118
+ }
119
+ } catch {
120
+ // Invalid URL after cleanup
121
+ }
122
+
123
+ return null;
124
+ }
125
+
126
+ /**
127
+ * Extract image URLs from tool result parts.
128
+ * Returns normalized, deduplicated URLs in order of first occurrence.
129
+ *
130
+ * @param toolData - Array of tool result parts
131
+ * @returns Array of unique normalized image URL strings
132
+ */
133
+ export function extractImageUrlsFromToolResult(toolData: ToolResultPart[]): string[] {
134
+ const seen = new Set<string>();
135
+ const imageUrls: string[] = [];
136
+
137
+ for (const part of toolData) {
138
+ const text = extractTextFromOutput(part.output);
139
+ if (!text) continue;
140
+
141
+ const matches = text.matchAll(new RegExp(URL_PATTERN.source, "gi"));
142
+ for (const match of matches) {
143
+ const result = normalizeImageUrl(match[0]);
144
+ if (result && !seen.has(result.normalizedUrl)) {
145
+ seen.add(result.normalizedUrl);
146
+ imageUrls.push(result.normalizedUrl);
147
+ }
148
+ }
149
+ }
150
+
151
+ return imageUrls;
152
+ }
153
+
154
+ /**
155
+ * Count the number of images in a tool result.
156
+ *
157
+ * @param toolData - Array of tool result parts
158
+ * @returns Number of images found
159
+ */
160
+ export function countImagesInToolResult(toolData: ToolResultPart[]): number {
161
+ return extractImageUrlsFromToolResult(toolData).length;
162
+ }
163
+
164
+ /**
165
+ * Result from processing tool results with image tracking
166
+ */
167
+ export interface ProcessToolResultOutput {
168
+ /** Processed tool result parts with placeholders applied */
169
+ processedParts: ToolResultPart[];
170
+ /** Number of individual URL occurrences replaced with placeholders */
171
+ replacedCount: number;
172
+ /** Number of unique URLs that were replaced (each may appear multiple times) */
173
+ uniqueReplacedCount: number;
174
+ }
175
+
176
+ /**
177
+ * Process a tool result with image tracking.
178
+ *
179
+ * - First-seen images: Preserved as-is, URL is marked as seen
180
+ * - Previously-seen images: URL replaced with placeholder text
181
+ *
182
+ * Uses a match-driven approach: finds all URL occurrences in text via regex,
183
+ * normalizes each match, and replaces ALL occurrences of seen URLs.
184
+ *
185
+ * @param toolData - Array of tool result parts
186
+ * @param tracker - Image tracker for the conversation
187
+ * @param eventId - Event ID for retrieval reference
188
+ * @returns Object with processed tool results and replacement statistics
189
+ */
190
+ export function processToolResultWithImageTracking(
191
+ toolData: ToolResultPart[],
192
+ tracker: ImageTracker,
193
+ eventId: string | undefined
194
+ ): ProcessToolResultOutput {
195
+ let totalReplacedCount = 0;
196
+ const uniqueReplacedUrls = new Set<string>();
197
+
198
+ const processedParts = toolData.map((part) => {
199
+ const text = extractTextFromOutput(part.output);
200
+ if (!text) {
201
+ // Non-text output (e.g., JSON), pass through unchanged
202
+ return part;
203
+ }
204
+
205
+ // Use regex replace callback for match-driven processing
206
+ // This ensures we find and process every URL occurrence in the text
207
+ const processedText = text.replace(
208
+ new RegExp(URL_PATTERN.source, "gi"),
209
+ (rawMatch) => {
210
+ const result = normalizeImageUrl(rawMatch);
211
+ if (!result) {
212
+ // Not a valid image URL, keep as-is
213
+ return rawMatch;
214
+ }
215
+
216
+ const { rawUrl, normalizedUrl } = result;
217
+
218
+ if (tracker.hasBeenSeen(normalizedUrl)) {
219
+ // Image already seen - replace with placeholder
220
+ totalReplacedCount++;
221
+ uniqueReplacedUrls.add(normalizedUrl);
222
+ return createImagePlaceholder(normalizedUrl, eventId);
223
+ } else {
224
+ // First time seeing this image - mark as seen but preserve URL
225
+ tracker.markAsSeen(normalizedUrl);
226
+ return rawUrl;
227
+ }
228
+ }
229
+ );
230
+
231
+ // Return modified part with updated text
232
+ return {
233
+ ...part,
234
+ output: updateOutputText(part.output, processedText),
235
+ };
236
+ });
237
+
238
+ return {
239
+ processedParts: processedParts as ToolResultPart[],
240
+ replacedCount: totalReplacedCount,
241
+ uniqueReplacedCount: uniqueReplacedUrls.size,
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Extract text from a tool result output (various formats)
247
+ */
248
+ function extractTextFromOutput(output: unknown): string | null {
249
+ if (typeof output === "string") {
250
+ return output;
251
+ }
252
+
253
+ if (output && typeof output === "object" && "value" in output) {
254
+ const value = (output as { value: unknown }).value;
255
+ if (typeof value === "string") {
256
+ return value;
257
+ }
258
+ }
259
+
260
+ return null;
261
+ }
262
+
263
+ /**
264
+ * Update the text in a tool result output, preserving the output structure
265
+ */
266
+ function updateOutputText(output: unknown, newText: string): unknown {
267
+ if (typeof output === "string") {
268
+ return newText;
269
+ }
270
+
271
+ if (output && typeof output === "object" && "value" in output) {
272
+ const existingOutput = output as { type?: string; value: unknown };
273
+ return {
274
+ ...existingOutput,
275
+ value: newText,
276
+ };
277
+ }
278
+
279
+ // Unknown format - return as-is
280
+ return output;
281
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Utilities for detecting and extracting image URLs from message content.
3
+ *
4
+ * These utilities help convert text messages containing image URLs into
5
+ * multimodal messages compatible with the AI SDK's ImagePart format.
6
+ */
7
+
8
+ /**
9
+ * Supported image file extensions (lowercase, with leading dot)
10
+ */
11
+ export const IMAGE_EXTENSIONS = [
12
+ ".jpg",
13
+ ".jpeg",
14
+ ".png",
15
+ ".gif",
16
+ ".webp",
17
+ ".svg",
18
+ ] as const;
19
+
20
+ /**
21
+ * Regex pattern to match HTTP(S) URLs
22
+ * Captures URLs that start with http:// or https://
23
+ */
24
+ const URL_PATTERN = /https?:\/\/[^\s<>"\]()]+/gi;
25
+
26
+ /**
27
+ * Domains that should be skipped for image fetching.
28
+ * These are either reserved example domains (RFC 2606), localhost/development domains,
29
+ * or other non-routable domains that will fail to fetch.
30
+ */
31
+ const SKIP_DOMAINS = new Set([
32
+ // RFC 2606 reserved example domains (only .com, .net, .org are reserved)
33
+ "example.com",
34
+ "example.org",
35
+ "example.net",
36
+ // Localhost variants
37
+ "localhost",
38
+ "0.0.0.0",
39
+ // IPv6 loopback - URL.hostname returns "[::1]" with brackets in Bun/Node
40
+ "[::1]",
41
+ // Common development/test domains
42
+ "test",
43
+ "invalid",
44
+ "local",
45
+ ]);
46
+
47
+ /**
48
+ * Check if a hostname is a loopback address (127.0.0.0/8 range).
49
+ * This covers all 127.x.x.x addresses, not just 127.0.0.1.
50
+ *
51
+ * @param hostname - The hostname to check
52
+ * @returns true if the hostname is a loopback address
53
+ */
54
+ function isLoopbackAddress(hostname: string): boolean {
55
+ // Match any 127.x.x.x address (the entire 127.0.0.0/8 block is reserved for loopback)
56
+ return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname);
57
+ }
58
+
59
+ /**
60
+ * Check if a URL should be skipped for image fetching.
61
+ * Returns true for URLs on reserved/example/localhost domains that
62
+ * will fail to fetch and could crash the agent.
63
+ *
64
+ * @param url - The URL string to check
65
+ * @returns true if the URL should be skipped, false if it can be fetched
66
+ */
67
+ export function shouldSkipImageUrl(url: string): boolean {
68
+ if (!url) return true;
69
+
70
+ try {
71
+ const parsedUrl = new URL(url);
72
+ const hostname = parsedUrl.hostname.toLowerCase();
73
+
74
+ // Check exact match against skip domains
75
+ if (SKIP_DOMAINS.has(hostname)) {
76
+ return true;
77
+ }
78
+
79
+ // Check for any 127.x.x.x loopback address (entire /8 block)
80
+ if (isLoopbackAddress(hostname)) {
81
+ return true;
82
+ }
83
+
84
+ // Check for subdomains of example domains (e.g., cdn.example.com, www.example.org)
85
+ for (const skipDomain of SKIP_DOMAINS) {
86
+ if (hostname.endsWith(`.${skipDomain}`)) {
87
+ return true;
88
+ }
89
+ }
90
+
91
+ // Check for .local, .test, .invalid, .localhost TLDs (RFC 2606 / RFC 6761)
92
+ if (
93
+ hostname.endsWith(".local") ||
94
+ hostname.endsWith(".test") ||
95
+ hostname.endsWith(".invalid") ||
96
+ hostname.endsWith(".localhost") ||
97
+ hostname.endsWith(".example")
98
+ ) {
99
+ return true;
100
+ }
101
+
102
+ return false;
103
+ } catch {
104
+ // Invalid URL - skip it
105
+ return true;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Check if a URL string points to an image based on its file extension.
111
+ * Handles URLs with query parameters and fragments.
112
+ *
113
+ * @param url - The URL string to check
114
+ * @returns true if the URL appears to be an image, false otherwise
115
+ */
116
+ export function isImageUrl(url: string): boolean {
117
+ if (!url) return false;
118
+
119
+ try {
120
+ const parsedUrl = new URL(url);
121
+ // Only accept http/https protocols
122
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
123
+ return false;
124
+ }
125
+
126
+ // Get the pathname and check the extension (ignoring query params and fragments)
127
+ const pathname = parsedUrl.pathname.toLowerCase();
128
+
129
+ return IMAGE_EXTENSIONS.some((ext) => pathname.endsWith(ext));
130
+ } catch {
131
+ // Invalid URL
132
+ return false;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Extract all image URLs from a text string.
138
+ * Returns deduplicated URLs in order of first occurrence.
139
+ *
140
+ * @param text - The text content to search for image URLs
141
+ * @returns Array of unique image URL strings
142
+ */
143
+ export function extractImageUrls(text: string): string[] {
144
+ if (!text) return [];
145
+
146
+ // Find all URLs in the text
147
+ const allUrls = text.match(URL_PATTERN) || [];
148
+
149
+ // Filter to only image URLs and deduplicate
150
+ const seen = new Set<string>();
151
+ const imageUrls: string[] = [];
152
+
153
+ for (const url of allUrls) {
154
+ // Clean up URL (remove trailing punctuation that might have been captured)
155
+ let cleanUrl = url.replace(/[),.]+$/, "");
156
+
157
+ try {
158
+ // Validate and normalize
159
+ cleanUrl = new URL(cleanUrl).href;
160
+ } catch {
161
+ continue; // Skip invalid URLs encountered during cleanup
162
+ }
163
+
164
+ if (isImageUrl(cleanUrl) && !seen.has(cleanUrl)) {
165
+ seen.add(cleanUrl);
166
+ imageUrls.push(cleanUrl);
167
+ }
168
+ }
169
+
170
+ return imageUrls;
171
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Utilities for converting text content with image URLs to AI SDK multimodal format.
3
+ *
4
+ * The AI SDK supports multimodal user messages with content arrays containing
5
+ * TextPart and ImagePart objects. This module converts plain text messages
6
+ * containing image URLs into the appropriate multimodal format.
7
+ *
8
+ * @see https://ai-sdk.dev/docs/foundations/prompts#multi-modal-messages
9
+ */
10
+
11
+ import type { TextPart, ImagePart } from "ai";
12
+ import { extractImageUrls, shouldSkipImageUrl } from "./image-url-utils";
13
+
14
+ /**
15
+ * Type representing multimodal content for user messages.
16
+ * Can be a simple string (no images) or an array of text/image parts.
17
+ */
18
+ export type MultimodalContent = string | Array<TextPart | ImagePart>;
19
+
20
+ /**
21
+ * Check if text content contains any image URLs.
22
+ * Use this to determine if multimodal conversion is needed.
23
+ *
24
+ * @param content - The text content to check
25
+ * @returns true if the content contains image URLs
26
+ */
27
+ export function hasImageUrls(content: string): boolean {
28
+ return extractImageUrls(content).length > 0;
29
+ }
30
+
31
+ /**
32
+ * Convert text content to multimodal content if it contains image URLs.
33
+ *
34
+ * If the content contains image URLs:
35
+ * - Returns an array with:
36
+ * 1. A TextPart containing the full original text (preserves context)
37
+ * 2. ImageParts for each unique image URL (using URL objects for AI SDK to fetch)
38
+ *
39
+ * If no image URLs are found:
40
+ * - Returns the original string unchanged
41
+ *
42
+ * @param content - The text content to convert
43
+ * @returns Either the original string or a multimodal content array
44
+ */
45
+ export function convertToMultimodalContent(content: string): MultimodalContent {
46
+ if (!content) return content;
47
+
48
+ const imageUrls = extractImageUrls(content);
49
+
50
+ // No images - return as-is
51
+ if (imageUrls.length === 0) {
52
+ return content;
53
+ }
54
+
55
+ // Build multimodal content array
56
+ const parts: Array<TextPart | ImagePart> = [];
57
+
58
+ // Add the full text content first (preserves context and attribution)
59
+ parts.push({
60
+ type: "text",
61
+ text: content,
62
+ } satisfies TextPart);
63
+
64
+ // Add each image as an ImagePart with URL reference
65
+ // The AI SDK will fetch the images automatically
66
+ // Skip URLs on non-routable domains (example.com, localhost, etc.) that would fail
67
+ for (const imageUrl of imageUrls) {
68
+ // Skip URLs that cannot be fetched (example domains, localhost, etc.)
69
+ // These would cause the AI SDK fetch to fail and crash the agent
70
+ if (shouldSkipImageUrl(imageUrl)) {
71
+ continue;
72
+ }
73
+
74
+ // Note: new URL() here should never throw because extractImageUrls
75
+ // already validates and normalizes URLs. The ImagePart uses URL objects
76
+ // as per AI SDK spec, allowing the SDK to fetch the images.
77
+ parts.push({
78
+ type: "image",
79
+ image: new URL(imageUrl),
80
+ } satisfies ImagePart);
81
+ }
82
+
83
+ // If all image URLs were skipped (non-fetchable domains), return original string
84
+ // parts[0] is always the TextPart, so if length is 1, no images were added
85
+ if (parts.length === 1) {
86
+ return content;
87
+ }
88
+
89
+ return parts;
90
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * ToolResultTruncator - Truncates large/old tool results to save context
3
+ *
4
+ * Rules:
5
+ * - Tool results < 1k chars: never truncated
6
+ * - Tool results > 10k chars: include inline for 3 messages, then truncate
7
+ * - Tool results 1k-10k chars: include inline for 6 messages, then truncate
8
+ *
9
+ * Truncated results are replaced with a reference that allows re-reading via
10
+ * the event ID using fs_read(tool=<eventId>).
11
+ *
12
+ * EMERGENCY CONTENT GUARD: If truncation criteria are met but no eventId is
13
+ * available, content is STILL omitted to protect the context window. This
14
+ * prioritizes context window safety over content retrieval.
15
+ */
16
+
17
+ import type { ToolResultPart } from "ai";
18
+
19
+ // Thresholds for truncation
20
+ const NEVER_TRUNCATE_THRESHOLD = 1000; // 1k chars - never truncate below this
21
+ const LARGE_RESULT_THRESHOLD = 10000; // 10k chars
22
+ const LARGE_RESULT_BURIAL_LIMIT = 3; // Messages before truncation for large results
23
+ const SMALL_RESULT_BURIAL_LIMIT = 6; // Messages before truncation for small results
24
+
25
+ export interface TruncationContext {
26
+ /** Current message index being processed */
27
+ currentIndex: number;
28
+ /** Total number of messages in the conversation */
29
+ totalMessages: number;
30
+ /** Event ID for referencing the tool result (required for truncation) */
31
+ eventId?: string;
32
+ }
33
+
34
+ /**
35
+ * Calculate the original size of a tool result
36
+ */
37
+ function getToolResultSize(toolData: ToolResultPart[]): number {
38
+ let totalSize = 0;
39
+ for (const part of toolData) {
40
+ const output = part.output as unknown;
41
+ if (output) {
42
+ if (typeof output === "string") {
43
+ totalSize += output.length;
44
+ } else if (typeof output === "object" && output !== null && "value" in output) {
45
+ const value = (output as { value: unknown }).value;
46
+ if (typeof value === "string") {
47
+ totalSize += value.length;
48
+ } else {
49
+ try {
50
+ totalSize += (JSON.stringify(value) ?? "").length;
51
+ } catch {
52
+ // Fallback for non-serializable values (BigInt, circular refs, etc.)
53
+ // Bias toward truncation when we can't measure - safer for context window
54
+ totalSize += LARGE_RESULT_THRESHOLD;
55
+ }
56
+ }
57
+ } else {
58
+ try {
59
+ totalSize += (JSON.stringify(output) ?? "").length;
60
+ } catch {
61
+ // Fallback for non-serializable values (BigInt, circular refs, etc.)
62
+ // Bias toward truncation when we can't measure - safer for context window
63
+ totalSize += LARGE_RESULT_THRESHOLD;
64
+ }
65
+ }
66
+ }
67
+ }
68
+ return totalSize;
69
+ }
70
+
71
+ /**
72
+ * Check if a tool result should be truncated based on its size and burial depth.
73
+ * EMERGENCY CONTENT GUARD: Truncation decision is independent of eventId availability.
74
+ * Context window safety takes priority over content retrieval.
75
+ */
76
+ export function shouldTruncateToolResult(
77
+ toolData: ToolResultPart[],
78
+ context: TruncationContext
79
+ ): boolean {
80
+ const size = getToolResultSize(toolData);
81
+
82
+ // Never truncate small results
83
+ if (size < NEVER_TRUNCATE_THRESHOLD) {
84
+ return false;
85
+ }
86
+
87
+ // NOTE: We no longer check for eventId here. Truncation decision is purely
88
+ // based on size and burial depth. The processToolResult function handles
89
+ // the case where eventId is missing by providing a placeholder message.
90
+
91
+ const burialDepth = context.totalMessages - context.currentIndex - 1;
92
+
93
+ if (size > LARGE_RESULT_THRESHOLD) {
94
+ return burialDepth >= LARGE_RESULT_BURIAL_LIMIT;
95
+ }
96
+
97
+ return burialDepth >= SMALL_RESULT_BURIAL_LIMIT;
98
+ }
99
+
100
+ /**
101
+ * Create a truncated tool result that references the original by event ID
102
+ */
103
+ export function createTruncatedToolResult(
104
+ toolData: ToolResultPart[],
105
+ eventId: string
106
+ ): ToolResultPart[] {
107
+ const size = getToolResultSize(toolData);
108
+
109
+ return toolData.map((part) => ({
110
+ type: "tool-result" as const,
111
+ toolCallId: part.toolCallId,
112
+ toolName: part.toolName,
113
+ output: {
114
+ type: "text" as const,
115
+ value: `[Tool executed, ${size} chars output truncated. Use fs_read(tool="${eventId}") to retrieve full output if needed]`,
116
+ },
117
+ }));
118
+ }
119
+
120
+ /**
121
+ * Create an omitted tool result when no eventId is available for retrieval.
122
+ * EMERGENCY CONTENT GUARD: This protects the context window from overflow
123
+ * when truncation criteria are met but content cannot be retrieved later.
124
+ */
125
+ export function createOmittedToolResult(toolData: ToolResultPart[]): ToolResultPart[] {
126
+ const size = getToolResultSize(toolData);
127
+
128
+ return toolData.map((part) => ({
129
+ type: "tool-result" as const,
130
+ toolCallId: part.toolCallId,
131
+ toolName: part.toolName,
132
+ output: {
133
+ type: "text" as const,
134
+ value: `[Tool output omitted to save context (${size} chars) - no reference available for retrieval]`,
135
+ },
136
+ }));
137
+ }
138
+
139
+ /**
140
+ * Process tool result, potentially truncating if buried too deep.
141
+ * EMERGENCY CONTENT GUARD: If truncation is needed but no eventId is available,
142
+ * content is omitted with a placeholder to protect the context window.
143
+ */
144
+ export function processToolResult(
145
+ toolData: ToolResultPart[],
146
+ context: TruncationContext
147
+ ): ToolResultPart[] {
148
+ if (shouldTruncateToolResult(toolData, context)) {
149
+ if (context.eventId) {
150
+ // Normal case: truncate with retrieval reference
151
+ return createTruncatedToolResult(toolData, context.eventId);
152
+ } else {
153
+ // EMERGENCY CONTENT GUARD: No eventId available, but we still
154
+ // must protect the context window. Omit content with placeholder.
155
+ return createOmittedToolResult(toolData);
156
+ }
157
+ }
158
+ return toolData;
159
+ }