@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,587 @@
1
+ import type { CompiledMessage } from "@/agents/execution/MessageCompiler";
2
+ import type { ConversationEntry } from "@/conversations/types";
3
+ import type {
4
+ CompressionSegment,
5
+ CompressionRange,
6
+ ValidationResult,
7
+ } from "./compression-types.js";
8
+
9
+ /**
10
+ * Estimate token count for messages using rough heuristic (chars/4).
11
+ * This is faster than actual tokenization and sufficient for compression decisions.
12
+ *
13
+ * @param messages - Messages to estimate
14
+ * @returns Estimated token count
15
+ */
16
+ export function estimateTokens(
17
+ messages: CompiledMessage[]
18
+ ): number {
19
+ const totalChars = messages.reduce((sum, msg) => {
20
+ const contentLength = typeof msg.content === "string"
21
+ ? msg.content.length
22
+ : JSON.stringify(msg.content).length;
23
+ return sum + contentLength;
24
+ }, 0);
25
+
26
+ // Rough estimate: 1 token ≈ 4 characters
27
+ return Math.ceil(totalChars / 4);
28
+ }
29
+
30
+ /**
31
+ * Select a range of messages to compress.
32
+ * Strategy: Skip recent messages (keep context fresh), compress older middle section.
33
+ *
34
+ * @param messages - All messages in conversation
35
+ * @param lastSegment - Most recent compression segment (to avoid re-compressing)
36
+ * @returns Range to compress, or null if no suitable range found
37
+ */
38
+ export function selectCandidateRange(
39
+ messages: CompiledMessage[],
40
+ lastSegment: CompressionSegment | null
41
+ ): CompressionRange | null {
42
+ if (messages.length < 10) {
43
+ // Too few messages to compress meaningfully
44
+ return null;
45
+ }
46
+
47
+ // Keep last 20% of messages uncompressed (fresh context)
48
+ const keepRecentCount = Math.max(5, Math.floor(messages.length * 0.2));
49
+ const endIndex = messages.length - keepRecentCount;
50
+
51
+ // Find start index (after last compressed segment, or from beginning)
52
+ let startIndex = 0;
53
+ if (lastSegment) {
54
+ const lastCompressedIndex = messages.findIndex(
55
+ (m) => m.eventId === lastSegment.toEventId
56
+ );
57
+ if (lastCompressedIndex >= 0) {
58
+ startIndex = lastCompressedIndex + 1;
59
+ }
60
+ }
61
+
62
+ // Need at least 5 messages to make compression worthwhile
63
+ if (endIndex - startIndex < 5) {
64
+ return null;
65
+ }
66
+
67
+ return { startIndex, endIndex };
68
+ }
69
+
70
+ /**
71
+ * Validate that LLM-generated segments are valid for the given message range.
72
+ *
73
+ * Checks:
74
+ * 1. All event IDs exist in the message range
75
+ * 2. Segments are in chronological order
76
+ * 3. No gaps or overlaps between segments
77
+ * 4. First segment starts at range beginning
78
+ * 5. Last segment ends at range end
79
+ *
80
+ * @param segments - LLM-generated segments
81
+ * @param messages - All messages
82
+ * @param range - The range that was compressed
83
+ * @returns Validation result
84
+ */
85
+ export function validateSegments(
86
+ segments: CompressionSegment[],
87
+ messages: CompiledMessage[],
88
+ range: CompressionRange
89
+ ): ValidationResult {
90
+ if (segments.length === 0) {
91
+ return { valid: false, error: "No segments provided" };
92
+ }
93
+
94
+ const rangeMessages = messages.slice(range.startIndex, range.endIndex);
95
+ const messagesWithEventIds = rangeMessages.filter((m) => m.eventId);
96
+
97
+ if (messagesWithEventIds.length === 0) {
98
+ return { valid: false, error: "No messages with eventIds in range" };
99
+ }
100
+
101
+ const eventIds = new Set(messagesWithEventIds.map((m) => m.eventId!));
102
+
103
+ // Check all event IDs exist
104
+ for (const segment of segments) {
105
+ if (!eventIds.has(segment.fromEventId)) {
106
+ return {
107
+ valid: false,
108
+ error: `fromEventId ${segment.fromEventId} not found in range`,
109
+ };
110
+ }
111
+ if (!eventIds.has(segment.toEventId)) {
112
+ return {
113
+ valid: false,
114
+ error: `toEventId ${segment.toEventId} not found in range`,
115
+ };
116
+ }
117
+ }
118
+
119
+ // Check that first segment starts at range start
120
+ const firstMessageWithEventId = messagesWithEventIds[0];
121
+ const firstSegment = segments[0];
122
+ if (firstSegment.fromEventId !== firstMessageWithEventId.eventId) {
123
+ return {
124
+ valid: false,
125
+ error: `First segment must start at range beginning (expected ${firstMessageWithEventId.eventId}, got ${firstSegment.fromEventId})`,
126
+ };
127
+ }
128
+
129
+ // Check that last segment ends at range end
130
+ const lastMessageWithEventId = messagesWithEventIds[messagesWithEventIds.length - 1];
131
+ const lastSegment = segments[segments.length - 1];
132
+ if (lastSegment.toEventId !== lastMessageWithEventId.eventId) {
133
+ return {
134
+ valid: false,
135
+ error: `Last segment must end at range end (expected ${lastMessageWithEventId.eventId}, got ${lastSegment.toEventId})`,
136
+ };
137
+ }
138
+
139
+ // Check chronological order and no gaps
140
+ for (let i = 0; i < segments.length; i++) {
141
+ const segment = segments[i];
142
+ const fromIndex = rangeMessages.findIndex(
143
+ (m) => m.eventId === segment.fromEventId
144
+ );
145
+ const toIndex = rangeMessages.findIndex(
146
+ (m) => m.eventId === segment.toEventId
147
+ );
148
+
149
+ if (fromIndex > toIndex) {
150
+ return {
151
+ valid: false,
152
+ error: `Segment ${i}: fromEventId comes after toEventId`,
153
+ };
154
+ }
155
+
156
+ // Check for gaps between segments
157
+ if (i > 0) {
158
+ const prevSegment = segments[i - 1];
159
+ const prevToIndex = rangeMessages.findIndex(
160
+ (m) => m.eventId === prevSegment.toEventId
161
+ );
162
+ if (fromIndex !== prevToIndex + 1) {
163
+ return {
164
+ valid: false,
165
+ error: `Gap between segment ${i - 1} and ${i}`,
166
+ };
167
+ }
168
+ }
169
+ }
170
+
171
+ return { valid: true };
172
+ }
173
+
174
+ /**
175
+ * Validate that LLM-generated segments are valid for the given entry range.
176
+ * This is the ConversationEntry version of validateSegments.
177
+ *
178
+ * Checks:
179
+ * 1. All event IDs exist in the entry range
180
+ * 2. Segments are in chronological order
181
+ * 3. No gaps or overlaps between segments
182
+ * 4. First segment starts at range beginning
183
+ * 5. Last segment ends at range end
184
+ *
185
+ * @param segments - LLM-generated segments
186
+ * @param entries - All entries
187
+ * @param range - The range that was compressed
188
+ * @returns Validation result
189
+ */
190
+ export function validateSegmentsForEntries(
191
+ segments: CompressionSegment[],
192
+ entries: ConversationEntry[],
193
+ range: CompressionRange
194
+ ): ValidationResult {
195
+ if (segments.length === 0) {
196
+ return { valid: false, error: "No segments provided" };
197
+ }
198
+
199
+ const rangeEntries = entries.slice(range.startIndex, range.endIndex);
200
+ const entriesWithEventIds = rangeEntries.filter((e) => e.eventId);
201
+
202
+ if (entriesWithEventIds.length === 0) {
203
+ return { valid: false, error: "No entries with eventIds in range" };
204
+ }
205
+
206
+ const eventIds = new Set(entriesWithEventIds.map((e) => e.eventId!));
207
+
208
+ // Check all event IDs exist
209
+ for (const segment of segments) {
210
+ if (!eventIds.has(segment.fromEventId)) {
211
+ return {
212
+ valid: false,
213
+ error: `fromEventId ${segment.fromEventId} not found in range`,
214
+ };
215
+ }
216
+ if (!eventIds.has(segment.toEventId)) {
217
+ return {
218
+ valid: false,
219
+ error: `toEventId ${segment.toEventId} not found in range`,
220
+ };
221
+ }
222
+ }
223
+
224
+ // Check that first segment starts at range start
225
+ const firstEntryWithEventId = entriesWithEventIds[0];
226
+ const firstSegment = segments[0];
227
+ if (firstSegment.fromEventId !== firstEntryWithEventId.eventId) {
228
+ return {
229
+ valid: false,
230
+ error: `First segment must start at range beginning (expected ${firstEntryWithEventId.eventId}, got ${firstSegment.fromEventId})`,
231
+ };
232
+ }
233
+
234
+ // Check that last segment ends at range end
235
+ const lastEntryWithEventId = entriesWithEventIds[entriesWithEventIds.length - 1];
236
+ const lastSegment = segments[segments.length - 1];
237
+ if (lastSegment.toEventId !== lastEntryWithEventId.eventId) {
238
+ return {
239
+ valid: false,
240
+ error: `Last segment must end at range end (expected ${lastEntryWithEventId.eventId}, got ${lastSegment.toEventId})`,
241
+ };
242
+ }
243
+
244
+ // Check chronological order and no gaps
245
+ for (let i = 0; i < segments.length; i++) {
246
+ const segment = segments[i];
247
+ const fromIndex = rangeEntries.findIndex(
248
+ (e) => e.eventId === segment.fromEventId
249
+ );
250
+ const toIndex = rangeEntries.findIndex(
251
+ (e) => e.eventId === segment.toEventId
252
+ );
253
+
254
+ if (fromIndex > toIndex) {
255
+ return {
256
+ valid: false,
257
+ error: `Segment ${i}: fromEventId comes after toEventId`,
258
+ };
259
+ }
260
+
261
+ // Check for gaps between segments
262
+ if (i > 0) {
263
+ const prevSegment = segments[i - 1];
264
+ const prevToIndex = rangeEntries.findIndex(
265
+ (e) => e.eventId === prevSegment.toEventId
266
+ );
267
+ if (fromIndex !== prevToIndex + 1) {
268
+ return {
269
+ valid: false,
270
+ error: `Gap between segment ${i - 1} and ${i}`,
271
+ };
272
+ }
273
+ }
274
+ }
275
+
276
+ return { valid: true };
277
+ }
278
+
279
+ /**
280
+ * Apply compression segments to messages, replacing ranges with summaries.
281
+ *
282
+ * Creates new "system" role messages with compressed content.
283
+ * Preserves all other messages unchanged.
284
+ *
285
+ * @param messages - Original messages
286
+ * @param segments - Compression segments to apply
287
+ * @returns New message array with compressions applied
288
+ */
289
+ export function applySegments(
290
+ messages: CompiledMessage[],
291
+ segments: CompressionSegment[]
292
+ ): CompiledMessage[] {
293
+ if (segments.length === 0) {
294
+ return messages;
295
+ }
296
+
297
+ const result: CompiledMessage[] = [];
298
+ let currentIndex = 0;
299
+
300
+ for (const segment of segments) {
301
+ const fromIndex = messages.findIndex(
302
+ (m) => m.eventId === segment.fromEventId
303
+ );
304
+ const toIndex = messages.findIndex((m) => m.eventId === segment.toEventId);
305
+
306
+ if (fromIndex < 0 || toIndex < 0) {
307
+ // Segment doesn't match current messages, skip it
308
+ continue;
309
+ }
310
+
311
+ // Add messages before this segment
312
+ while (currentIndex < fromIndex) {
313
+ result.push(messages[currentIndex]);
314
+ currentIndex++;
315
+ }
316
+
317
+ // Add compressed summary as system message
318
+ result.push({
319
+ role: "system",
320
+ content: `[Compressed history]\n${segment.compressed}`,
321
+ eventId: `compressed-${segment.fromEventId}-${segment.toEventId}`,
322
+ });
323
+
324
+ // Skip the compressed range
325
+ currentIndex = toIndex + 1;
326
+ }
327
+
328
+ // Add remaining messages after last segment
329
+ while (currentIndex < messages.length) {
330
+ result.push(messages[currentIndex]);
331
+ currentIndex++;
332
+ }
333
+
334
+ return result;
335
+ }
336
+
337
+ /**
338
+ * Emergency fallback: truncate to sliding window of recent messages.
339
+ * Used when LLM compression fails or is unavailable.
340
+ *
341
+ * NOTE: This utility is currently unused in production but maintained as part of
342
+ * the public API for future use cases (e.g., client-side compression, direct
343
+ * truncation without segment creation). See createFallbackSegmentForEntries for
344
+ * the production fallback implementation.
345
+ *
346
+ * @param messages - All messages
347
+ * @param windowSize - Number of recent messages to keep
348
+ * @param currentTimestamp - Current timestamp (for deterministic testing)
349
+ * @returns Truncated message array
350
+ */
351
+ export function truncateSlidingWindow(
352
+ messages: CompiledMessage[],
353
+ windowSize: number,
354
+ currentTimestamp: number
355
+ ): CompiledMessage[] {
356
+ if (messages.length <= windowSize) {
357
+ return messages;
358
+ }
359
+
360
+ const kept = messages.slice(-windowSize);
361
+
362
+ // Add a system message at the start indicating truncation
363
+ return [
364
+ {
365
+ role: "system",
366
+ content: `[Earlier messages truncated. Showing last ${windowSize} messages.]`,
367
+ eventId: `truncated-${currentTimestamp}`,
368
+ },
369
+ ...kept,
370
+ ];
371
+ }
372
+
373
+ /**
374
+ * Apply compression segments to conversation entries, replacing ranges with summaries.
375
+ * This is the ConversationEntry version of applySegments.
376
+ *
377
+ * Creates new "system" entries with compressed content.
378
+ * Preserves all other entries unchanged.
379
+ *
380
+ * @param entries - Original entries
381
+ * @param segments - Compression segments to apply
382
+ * @returns New entry array with compressions applied
383
+ */
384
+ export function applySegmentsToEntries(
385
+ entries: ConversationEntry[],
386
+ segments: CompressionSegment[]
387
+ ): ConversationEntry[] {
388
+ if (segments.length === 0) {
389
+ return entries;
390
+ }
391
+
392
+ const result: ConversationEntry[] = [];
393
+ let currentIndex = 0;
394
+
395
+ for (const segment of segments) {
396
+ const fromIndex = entries.findIndex((e) => e.eventId === segment.fromEventId);
397
+ const toIndex = entries.findIndex((e) => e.eventId === segment.toEventId);
398
+
399
+ if (fromIndex < 0 || toIndex < 0) {
400
+ // Segment doesn't match current entries, skip it
401
+ continue;
402
+ }
403
+
404
+ // Add entries before this segment
405
+ while (currentIndex < fromIndex) {
406
+ result.push(entries[currentIndex]);
407
+ currentIndex++;
408
+ }
409
+
410
+ // Add compressed summary as system entry with explicit role
411
+ // CRITICAL: role field prevents deriveRole from misclassifying as "user"
412
+ result.push({
413
+ pubkey: "system",
414
+ content: `[Compressed history]\n${segment.compressed}`,
415
+ messageType: "text",
416
+ eventId: `compressed-${segment.fromEventId}-${segment.toEventId}`,
417
+ timestamp: segment.createdAt / 1000,
418
+ role: "system",
419
+ });
420
+
421
+ // Skip the compressed range
422
+ currentIndex = toIndex + 1;
423
+ }
424
+
425
+ // Add remaining entries after last segment
426
+ while (currentIndex < entries.length) {
427
+ result.push(entries[currentIndex]);
428
+ currentIndex++;
429
+ }
430
+
431
+ return result;
432
+ }
433
+
434
+ /**
435
+ * Estimate token count for conversation entries using rough heuristic (chars/4).
436
+ * This is faster than actual tokenization and sufficient for compression decisions.
437
+ *
438
+ * Accounts for both text content and tool payloads (toolData) to prevent
439
+ * tool-heavy conversations from bypassing compression thresholds.
440
+ *
441
+ * @param entries - Entries to estimate
442
+ * @returns Estimated token count
443
+ */
444
+ export function estimateTokensFromEntries(entries: ConversationEntry[]): number {
445
+ const totalChars = entries.reduce((sum, entry) => {
446
+ let entryChars = entry.content.length;
447
+
448
+ // Account for tool payloads (tool-call/tool-result)
449
+ if (entry.toolData && entry.toolData.length > 0) {
450
+ const toolDataSize = JSON.stringify(entry.toolData).length;
451
+ entryChars += toolDataSize;
452
+ }
453
+
454
+ return sum + entryChars;
455
+ }, 0);
456
+ return Math.ceil(totalChars / 4);
457
+ }
458
+
459
+ /**
460
+ * Select a range of entries to compress.
461
+ * Strategy: Skip recent entries (keep context fresh), compress older middle section.
462
+ *
463
+ * @param entries - All entries in conversation
464
+ * @param lastSegment - Most recent compression segment (to avoid re-compressing)
465
+ * @returns Range to compress, or null if no suitable range found
466
+ */
467
+ export function selectCandidateRangeFromEntries(
468
+ entries: ConversationEntry[],
469
+ lastSegment: CompressionSegment | null
470
+ ): CompressionRange | null {
471
+ if (entries.length < 10) {
472
+ // Too few entries to compress meaningfully
473
+ return null;
474
+ }
475
+
476
+ // Keep last 20% of entries uncompressed (fresh context)
477
+ const keepRecentCount = Math.max(5, Math.floor(entries.length * 0.2));
478
+ const endIndex = entries.length - keepRecentCount;
479
+
480
+ // Find start index (after last compressed segment, or from beginning)
481
+ let startIndex = 0;
482
+ if (lastSegment) {
483
+ const lastCompressedIndex = entries.findIndex(
484
+ (e) => e.eventId === lastSegment.toEventId
485
+ );
486
+ if (lastCompressedIndex >= 0) {
487
+ startIndex = lastCompressedIndex + 1;
488
+ }
489
+ }
490
+
491
+ // Need at least 5 entries to make compression worthwhile
492
+ if (endIndex - startIndex < 5) {
493
+ return null;
494
+ }
495
+
496
+ return { startIndex, endIndex };
497
+ }
498
+
499
+ /**
500
+ * Emergency fallback: truncate entries to sliding window.
501
+ * This is the ConversationEntry version of truncateSlidingWindow.
502
+ *
503
+ * NOTE: This utility is currently unused in production but maintained as part of
504
+ * the public API for future use cases (e.g., direct truncation without segment
505
+ * creation). See createFallbackSegmentForEntries for the production fallback.
506
+ *
507
+ * @param entries - All entries
508
+ * @param windowSize - Number of recent entries to keep
509
+ * @param currentTimestamp - Current timestamp (for deterministic testing)
510
+ * @returns Truncated entry array
511
+ */
512
+ export function truncateSlidingWindowEntries(
513
+ entries: ConversationEntry[],
514
+ windowSize: number,
515
+ currentTimestamp: number
516
+ ): ConversationEntry[] {
517
+ if (entries.length <= windowSize) {
518
+ return entries;
519
+ }
520
+
521
+ const kept = entries.slice(-windowSize);
522
+
523
+ // Add a synthetic entry indicating truncation
524
+ const truncationEntry: ConversationEntry = {
525
+ pubkey: "system",
526
+ content: `[Earlier messages truncated. Showing last ${windowSize} messages.]`,
527
+ messageType: "text",
528
+ timestamp: currentTimestamp / 1000,
529
+ };
530
+
531
+ return [truncationEntry, ...kept];
532
+ }
533
+
534
+ /**
535
+ * Create a fallback compression segment for entries using index-based approach.
536
+ * Works even when entries lack eventIds by finding the nearest entries with eventIds.
537
+ *
538
+ * @param entries - All entries in conversation
539
+ * @param windowSize - Number of recent entries to keep uncompressed
540
+ * @returns Fallback segment or null if cannot create one
541
+ */
542
+ export function createFallbackSegmentForEntries(
543
+ entries: ConversationEntry[],
544
+ windowSize: number
545
+ ): CompressionSegment | null {
546
+ if (entries.length <= windowSize) {
547
+ // Nothing to compress
548
+ return null;
549
+ }
550
+
551
+ // Find entries with eventIds
552
+ const entriesWithEventIds = entries
553
+ .map((e, index) => ({ entry: e, index }))
554
+ .filter((item) => item.entry.eventId);
555
+
556
+ if (entriesWithEventIds.length < 2) {
557
+ // Need at least 2 event IDs to create a valid segment
558
+ return null;
559
+ }
560
+
561
+ // Calculate how many entries to compress (before sliding window)
562
+ const truncateCount = entries.length - windowSize;
563
+
564
+ // Find the first entry with an eventId
565
+ const fromEventId = entriesWithEventIds[0].entry.eventId!;
566
+
567
+ // Find the last entry with eventId in the range to compress
568
+ // Use the entry at or before truncateCount-1
569
+ let toEntry = entriesWithEventIds[0];
570
+ for (const item of entriesWithEventIds) {
571
+ if (item.index < truncateCount) {
572
+ toEntry = item;
573
+ } else {
574
+ break;
575
+ }
576
+ }
577
+
578
+ const toEventId = toEntry.entry.eventId!;
579
+
580
+ return {
581
+ fromEventId,
582
+ toEventId,
583
+ compressed: `[Truncated ${truncateCount} earlier messages due to compression failure]`,
584
+ createdAt: Date.now(),
585
+ model: "fallback-truncation",
586
+ };
587
+ }