@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,597 @@
1
+ import type { AgentInstance } from "@/agents/types";
2
+ import type { ConversationStore } from "@/conversations/ConversationStore";
3
+ import type { NDKAgentLesson } from "@/events/NDKAgentLesson";
4
+ import { providerRegistry } from "@/llm/providers";
5
+ import type { ProviderCapabilities } from "@/llm/providers/types";
6
+ import { shortenConversationId } from "@/utils/conversation-id";
7
+ import { agentTodosFragment } from "@/prompts/fragments/06-agent-todos";
8
+ import { buildSystemPromptMessages } from "@/prompts/utils/systemPromptBuilder";
9
+ import { getPubkeyService } from "@/services/PubkeyService";
10
+ import type { CompletedDelegation, PendingDelegation } from "@/services/ral/types";
11
+ import type { MCPManager } from "@/services/mcp/MCPManager";
12
+ import type { NudgeToolPermissions, NudgeData, WhitelistItem } from "@/services/nudge";
13
+ import type { LessonComment } from "@/services/prompt-compiler";
14
+ import type { SkillData } from "@/services/skill";
15
+ import { combineSystemReminders } from "@/services/system-reminder";
16
+ import type { NDKProject } from "@nostr-dev-kit/ndk";
17
+ import type { ModelMessage, TextPart } from "ai";
18
+ import { SpanStatusCode, trace } from "@opentelemetry/api";
19
+ import type { SessionManager } from "./SessionManager";
20
+ import type { LLMService } from "@/llm/service";
21
+ import { createCompressionService } from "@/services/compression/CompressionService.js";
22
+ import { logger } from "@/utils/logger";
23
+
24
+ const tracer = trace.getTracer("tenex.message-compiler");
25
+
26
+ type CompilationMode = "full" | "delta";
27
+
28
+ interface MessageCompilationPlan {
29
+ mode: CompilationMode;
30
+ cursor?: number;
31
+ }
32
+
33
+ /** Ephemeral message to include in LLM context but NOT persist */
34
+ export interface EphemeralMessage {
35
+ role: "user" | "system";
36
+ content: string;
37
+ }
38
+
39
+ /**
40
+ * CompiledMessage - A message with event ID for compression tracking.
41
+ * Combines ModelMessage with optional eventId field.
42
+ */
43
+ export type CompiledMessage = ModelMessage & {
44
+ eventId?: string;
45
+ };
46
+
47
+ export interface MessageCompilerContext {
48
+ agent: AgentInstance;
49
+ project: NDKProject;
50
+ conversation: ConversationStore;
51
+ projectBasePath?: string;
52
+ workingDirectory?: string;
53
+ currentBranch?: string;
54
+ availableAgents?: AgentInstance[];
55
+ agentLessons?: Map<string, NDKAgentLesson[]>;
56
+ /** Comments on agent lessons (kind 1111 NIP-22 comments) */
57
+ agentComments?: Map<string, LessonComment[]>;
58
+ mcpManager?: MCPManager;
59
+ nudgeContent?: string;
60
+ /** Individual nudge data for rendering in fragments */
61
+ nudges?: NudgeData[];
62
+ /** Tool permissions extracted from nudge events */
63
+ nudgeToolPermissions?: NudgeToolPermissions;
64
+ /** Concatenated skill content */
65
+ skillContent?: string;
66
+ /** Individual skill data for rendering in fragments */
67
+ skills?: SkillData[];
68
+ respondingToPubkey: string;
69
+ pendingDelegations: PendingDelegation[];
70
+ completedDelegations: CompletedDelegation[];
71
+ ralNumber: number;
72
+ /** System prompt fragment describing available meta model variants */
73
+ metaModelSystemPrompt?: string;
74
+ /** Variant-specific system prompt to inject when a meta model variant is active */
75
+ variantSystemPrompt?: string;
76
+ /** Ephemeral messages to include in this compilation only (not persisted) */
77
+ ephemeralMessages?: EphemeralMessage[];
78
+ /** Available whitelisted nudges for delegation */
79
+ availableNudges?: WhitelistItem[];
80
+ /** Available whitelisted skills */
81
+ availableSkills?: WhitelistItem[];
82
+ }
83
+
84
+ export interface CompiledMessages {
85
+ messages: ModelMessage[];
86
+ mode: CompilationMode;
87
+ counts: {
88
+ systemPrompt: number;
89
+ conversation: number;
90
+ dynamicContext: number;
91
+ total: number;
92
+ };
93
+ }
94
+
95
+ export class MessageCompiler {
96
+ private readonly plan: MessageCompilationPlan;
97
+ private currentCursor: number;
98
+
99
+ constructor(
100
+ private providerId: string,
101
+ private sessionManager: SessionManager,
102
+ private conversationStore: ConversationStore,
103
+ private llmService?: LLMService,
104
+ private compressionLlmService?: LLMService
105
+ ) {
106
+ this.plan = this.buildPlan();
107
+ this.currentCursor = this.plan.cursor ?? -1;
108
+ }
109
+
110
+ async compile(context: MessageCompilerContext): Promise<CompiledMessages> {
111
+ return tracer.startActiveSpan("tenex.message.compile", async (span) => {
112
+ try {
113
+ // Validate conversation source - context.conversation must match conversationStore
114
+ if (context.conversation.id !== this.conversationStore.getId()) {
115
+ throw new Error(
116
+ `Conversation mismatch: context.conversation.id (${context.conversation.id}) ` +
117
+ `does not match conversationStore.id (${this.conversationStore.getId()})`
118
+ );
119
+ }
120
+
121
+ span.setAttribute("agent.slug", context.agent.slug);
122
+ span.setAttribute("conversation.id", shortenConversationId(context.conversation.id));
123
+ span.setAttribute("ral.number", context.ralNumber);
124
+ span.setAttribute("compilation.mode", this.plan.mode);
125
+
126
+ // Consume any deferred injections from previous turns (e.g., supervision nudges).
127
+ // These are added to ephemeral messages so they appear in context but aren't persisted.
128
+ const deferredInjections = this.conversationStore.consumeDeferredInjections(context.agent.pubkey);
129
+ if (deferredInjections.length > 0) {
130
+ span.setAttribute("deferred_injections.count", deferredInjections.length);
131
+ logger.debug("[MessageCompiler] Consuming deferred injections", {
132
+ agent: context.agent.slug,
133
+ count: deferredInjections.length,
134
+ sources: deferredInjections.map(d => d.source).filter(Boolean),
135
+ });
136
+
137
+ // Initialize ephemeralMessages if not present
138
+ if (!context.ephemeralMessages) {
139
+ context.ephemeralMessages = [];
140
+ }
141
+ // Add deferred injections as ephemeral system messages
142
+ for (const injection of deferredInjections) {
143
+ context.ephemeralMessages.push({
144
+ role: injection.role,
145
+ content: injection.content,
146
+ });
147
+ }
148
+ // Save the store to persist the consumption
149
+ await this.conversationStore.save();
150
+ }
151
+
152
+ const cursor = this.currentCursor;
153
+ const messages: ModelMessage[] = [];
154
+ let systemPromptCount = 0;
155
+ let dynamicContextCount = 0;
156
+
157
+ if (this.plan.mode === "full") {
158
+ // Build system prompt messages (sub-span removed - parent compile span is sufficient)
159
+ const systemPromptMessages = await buildSystemPromptMessages(context);
160
+
161
+ // Apply compression: load existing segments and apply to conversation history
162
+ await this.applyCompression(context);
163
+
164
+ // Build conversation history (sub-span removed - parent compile span is sufficient)
165
+ const conversationMessages = await this.conversationStore.buildMessagesForRal(
166
+ context.agent.pubkey,
167
+ context.ralNumber,
168
+ context.projectBasePath
169
+ );
170
+
171
+ // Build dynamic context content (todo state, response context)
172
+ const dynamicContextContent = await this.buildDynamicContextContent(context);
173
+
174
+ messages.push(...systemPromptMessages.map((sm) => sm.message));
175
+
176
+ // Inject meta model system prompts if present
177
+ // These describe available model variants and variant-specific instructions
178
+ if (context.metaModelSystemPrompt) {
179
+ messages.push({
180
+ role: "system",
181
+ content: context.metaModelSystemPrompt,
182
+ });
183
+ systemPromptCount++;
184
+ }
185
+ if (context.variantSystemPrompt) {
186
+ messages.push({
187
+ role: "system",
188
+ content: context.variantSystemPrompt,
189
+ });
190
+ systemPromptCount++;
191
+ }
192
+
193
+ messages.push(...conversationMessages);
194
+
195
+ // Collect all ephemeral content: dynamic context + any queued ephemeral messages.
196
+ // These are appended to the last user message as <system-reminder> tags.
197
+ // This unifies behavioral nudges (heuristic violations, deferred injections,
198
+ // todo state, response context) into a consistent format.
199
+ // AGENTS.md injections remain tool-bound (in tool result output).
200
+ //
201
+ // NOTE: dynamicContextCount is NOT incremented here because ephemeral content
202
+ // is appended to an existing user message (not added as separate messages).
203
+ // The count tracks messages in the array, not ephemeral injections.
204
+ const allEphemeralMessages: EphemeralMessage[] = [];
205
+
206
+ // Add dynamic context as ephemeral
207
+ if (dynamicContextContent) {
208
+ allEphemeralMessages.push({
209
+ role: "system",
210
+ content: dynamicContextContent,
211
+ });
212
+ }
213
+
214
+ // Add queued ephemeral messages (heuristic violations, deferred injections, etc.)
215
+ if (context.ephemeralMessages?.length) {
216
+ allEphemeralMessages.push(...context.ephemeralMessages);
217
+ }
218
+
219
+ // Append all ephemeral content to the last user message
220
+ if (allEphemeralMessages.length > 0) {
221
+ this.appendEphemeralMessagesToLastUserMessage(messages, allEphemeralMessages);
222
+ }
223
+
224
+ systemPromptCount += systemPromptMessages.length;
225
+ // NOTE: dynamicContextCount stays 0 because ephemeral content is appended to
226
+ // existing user messages, not added as separate messages. The count is retained
227
+ // in the interface for backwards compatibility with telemetry consumers.
228
+ } else {
229
+ // In delta mode, only send new conversation messages.
230
+ // The session already has full context from initial compilation.
231
+ // However, dynamic context (todos, response routing) must still be included
232
+ // since it can change between turns in a stateful session.
233
+ const conversationMessages = await this.conversationStore.buildMessagesForRalAfterIndex(
234
+ context.agent.pubkey,
235
+ context.ralNumber,
236
+ cursor,
237
+ context.projectBasePath
238
+ );
239
+ messages.push(...conversationMessages);
240
+
241
+ // Build dynamic context for delta mode (todo state, response context)
242
+ const dynamicContextContent = await this.buildDynamicContextContent(context);
243
+
244
+ // Collect all ephemeral content for delta mode
245
+ const allEphemeralMessages: EphemeralMessage[] = [];
246
+
247
+ if (dynamicContextContent) {
248
+ allEphemeralMessages.push({
249
+ role: "system",
250
+ content: dynamicContextContent,
251
+ });
252
+ }
253
+
254
+ if (context.ephemeralMessages?.length) {
255
+ allEphemeralMessages.push(...context.ephemeralMessages);
256
+ }
257
+
258
+ // Append all ephemeral content to the last user message
259
+ if (allEphemeralMessages.length > 0) {
260
+ this.appendEphemeralMessagesToLastUserMessage(messages, allEphemeralMessages);
261
+ }
262
+ }
263
+
264
+ const conversationCount = this.plan.mode === "full"
265
+ ? messages.length - systemPromptCount - dynamicContextCount
266
+ : messages.length;
267
+
268
+ const counts = {
269
+ systemPrompt: systemPromptCount,
270
+ conversation: conversationCount,
271
+ dynamicContext: dynamicContextCount,
272
+ total: messages.length,
273
+ };
274
+
275
+ span.setAttribute("message.count", messages.length);
276
+ span.setAttribute("counts.system_prompt", counts.systemPrompt);
277
+ span.setAttribute("counts.conversation", counts.conversation);
278
+ span.setAttribute("counts.dynamic_context", counts.dynamicContext);
279
+ span.setAttribute("counts.total", counts.total);
280
+
281
+ // Update cursor for subsequent prepareStep calls within same execution.
282
+ // This prevents resending the same messages during streaming.
283
+ // CRITICAL: Use compressed count, not raw count. Cursor must be in compressed space
284
+ // to match the space used by buildMessagesForRalAfterIndex.
285
+ this.currentCursor = Math.max(this.conversationStore.getCompressedMessageCount() - 1, cursor);
286
+
287
+ return { messages, mode: this.plan.mode, counts };
288
+ } catch (error) {
289
+ span.recordException(error as Error);
290
+ span.setStatus({ code: SpanStatusCode.ERROR });
291
+ throw error;
292
+ } finally {
293
+ span.end();
294
+ }
295
+ });
296
+ }
297
+
298
+ advanceCursor(): void {
299
+ const sessionCapabilities = this.getProviderCapabilities();
300
+ const isStateful = sessionCapabilities?.sessionResumption === true;
301
+
302
+ if (!isStateful) {
303
+ return;
304
+ }
305
+
306
+ // Use current message count (includes agent's response), not compile-time cursor.
307
+ // advanceCursor is called after agent responds, so we want the cursor to point
308
+ // to the last message the agent knows about (its own response).
309
+ // CRITICAL: Use compressed count to save cursor in compressed space.
310
+ const messageCount = this.conversationStore.getCompressedMessageCount();
311
+ const cursorToSave = messageCount - 1;
312
+ this.sessionManager.saveLastSentMessageIndex(cursorToSave);
313
+ }
314
+
315
+ private buildPlan(): MessageCompilationPlan {
316
+ const session = this.sessionManager.getSession();
317
+ const capabilities = this.getProviderCapabilities();
318
+ const isStateful = capabilities?.sessionResumption === true;
319
+ const hasSession = Boolean(session.sessionId);
320
+ const cursor = session.lastSentMessageIndex;
321
+ const cursorIsValid =
322
+ typeof cursor === "number" && cursor < this.conversationStore.getCompressedMessageCount();
323
+
324
+ if (isStateful && hasSession && cursorIsValid) {
325
+ return { mode: "delta", cursor };
326
+ }
327
+
328
+ return { mode: "full" };
329
+ }
330
+
331
+ private getProviderCapabilities(): ProviderCapabilities | undefined {
332
+ const normalized = this.normalizeProviderId(this.providerId);
333
+ const provider = providerRegistry.getProvider(normalized);
334
+ if (provider) {
335
+ return provider.metadata.capabilities;
336
+ }
337
+
338
+ const registered = providerRegistry
339
+ .getRegisteredProviders()
340
+ .find((metadata) => metadata.id === normalized);
341
+ return registered?.capabilities;
342
+ }
343
+
344
+ private normalizeProviderId(providerId: string): string {
345
+ const normalized = providerId.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
346
+ const registered = providerRegistry.getRegisteredProviders();
347
+ const matches = registered.some((metadata) => metadata.id === normalized);
348
+
349
+ return matches ? normalized : providerId;
350
+ }
351
+
352
+ /**
353
+ * Build dynamic context content for injection into the last user message.
354
+ * This includes todo state and response context.
355
+ *
356
+ * @returns Combined content string, or empty string if no content
357
+ */
358
+ private async buildDynamicContextContent(
359
+ context: MessageCompilerContext
360
+ ): Promise<string> {
361
+ const parts: string[] = [];
362
+
363
+ // Add todo content if present
364
+ const todoContent = await agentTodosFragment.template({
365
+ conversation: context.conversation,
366
+ agentPubkey: context.agent.pubkey,
367
+ });
368
+ if (todoContent) {
369
+ parts.push(todoContent);
370
+ }
371
+
372
+ // Always add response context
373
+ const responseContextContent = await this.buildResponseContext(context);
374
+ parts.push(responseContextContent);
375
+
376
+ return parts.join("\n\n");
377
+ }
378
+
379
+ private async buildResponseContext(context: MessageCompilerContext): Promise<string> {
380
+ const pubkeyService = getPubkeyService();
381
+ const respondingToName = await pubkeyService.getName(context.respondingToPubkey);
382
+ let responseContextContent = `Your response will be sent to @${respondingToName}.`;
383
+
384
+ const allDelegatedPubkeys = [
385
+ ...context.pendingDelegations.map((d) => d.recipientPubkey),
386
+ ...context.completedDelegations.map((d) => d.recipientPubkey),
387
+ ];
388
+
389
+ if (allDelegatedPubkeys.length > 0) {
390
+ const delegatedAgentNames = await Promise.all(
391
+ allDelegatedPubkeys.map((pk) => pubkeyService.getName(pk))
392
+ );
393
+ const uniqueNames = [...new Set(delegatedAgentNames)];
394
+ responseContextContent +=
395
+ `\nYou have delegations to: ${uniqueNames.map((n) => `@${n}`).join(", ")}.`;
396
+ responseContextContent +=
397
+ "\nIf you want to follow up with a delegated agent, use delegate_followup with the delegation ID. Do NOT address them directly in your response - they won't see it.";
398
+ }
399
+
400
+ return responseContextContent;
401
+ }
402
+
403
+ /**
404
+ * Append ephemeral messages to the last user message as <system-reminder> tags.
405
+ *
406
+ * This method finds the last user message in the array and appends all ephemeral
407
+ * messages as system-reminder blocks. This unifies behavioral nudges (heuristic
408
+ * violations, deferred injections, supervision corrections) into a consistent format.
409
+ *
410
+ * Handles both string content and multimodal content (TextPart + ImagePart arrays).
411
+ * For multimodal messages, the reminder is appended to the last text part.
412
+ *
413
+ * If no user message exists, the ephemeral messages are added as a new user message.
414
+ *
415
+ * @param messages - The message array to modify in place
416
+ * @param ephemeralMessages - The ephemeral messages to append
417
+ */
418
+ private appendEphemeralMessagesToLastUserMessage(
419
+ messages: ModelMessage[],
420
+ ephemeralMessages: EphemeralMessage[]
421
+ ): void {
422
+ if (ephemeralMessages.length === 0) {
423
+ return;
424
+ }
425
+
426
+ // Collect all ephemeral content, extracting inner content from already-wrapped reminders
427
+ const reminderContents: string[] = [];
428
+ for (const ephemeral of ephemeralMessages) {
429
+ const content = ephemeral.content.trim();
430
+ if (content) {
431
+ // If content already has system-reminder tags, extract the inner content
432
+ // to avoid double-wrapping when we combine them
433
+ if (content.startsWith("<system-reminder>")) {
434
+ // Extract all system-reminder blocks from the content
435
+ const extracted = this.extractAllSystemReminderContents(content);
436
+ if (extracted.length > 0) {
437
+ reminderContents.push(...extracted);
438
+ } else {
439
+ // Fallback: use raw content if extraction fails
440
+ reminderContents.push(content);
441
+ }
442
+ } else {
443
+ reminderContents.push(content);
444
+ }
445
+ }
446
+ }
447
+
448
+ if (reminderContents.length === 0) {
449
+ return;
450
+ }
451
+
452
+ // Combine all contents into a single system-reminder block
453
+ const combinedReminder = combineSystemReminders(reminderContents);
454
+
455
+ // Find the last user message (searching from the end)
456
+ for (let i = messages.length - 1; i >= 0; i--) {
457
+ const msg = messages[i];
458
+ if (msg.role === "user") {
459
+ if (typeof msg.content === "string") {
460
+ // String content: append directly
461
+ messages[i] = {
462
+ ...msg,
463
+ content: `${msg.content}\n\n${combinedReminder}`,
464
+ };
465
+ return;
466
+ } else if (Array.isArray(msg.content)) {
467
+ // Multimodal content: find the last text part and append to it
468
+ // We need to work with the array generically since content can be various part types
469
+ const contentArray = msg.content;
470
+ let lastTextIndex = -1;
471
+
472
+ // Find the last text part
473
+ for (let j = contentArray.length - 1; j >= 0; j--) {
474
+ const part = contentArray[j];
475
+ if (typeof part === "object" && part !== null && "type" in part &&
476
+ part.type === "text" && "text" in part && typeof part.text === "string") {
477
+ lastTextIndex = j;
478
+ break;
479
+ }
480
+ }
481
+
482
+ if (lastTextIndex >= 0) {
483
+ // Append to the last text part
484
+ const textPart = contentArray[lastTextIndex] as TextPart;
485
+ const newTextPart: TextPart = {
486
+ type: "text",
487
+ text: `${textPart.text}\n\n${combinedReminder}`,
488
+ };
489
+ // Create new array with modified text part
490
+ const newContent = contentArray.map((part, idx) =>
491
+ idx === lastTextIndex ? newTextPart : part
492
+ );
493
+ messages[i] = {
494
+ ...msg,
495
+ content: newContent,
496
+ };
497
+ return;
498
+ }
499
+ // No text part found in multimodal - add a text part
500
+ const newTextPart: TextPart = { type: "text", text: combinedReminder };
501
+ messages[i] = {
502
+ ...msg,
503
+ content: [...contentArray, newTextPart],
504
+ };
505
+ return;
506
+ }
507
+ }
508
+ }
509
+
510
+ // No user message found - add as a new user message
511
+ // This is a fallback; in practice there should always be a user message
512
+ logger.warn("[MessageCompiler] No user message found for ephemeral injection, adding as new message");
513
+ messages.push({
514
+ role: "user",
515
+ content: combinedReminder,
516
+ });
517
+ }
518
+
519
+ /**
520
+ * Extract all content blocks from system-reminder tags.
521
+ * Handles multiple consecutive system-reminder blocks and trailing text.
522
+ *
523
+ * @param content - String potentially containing system-reminder tags
524
+ * @returns Array of extracted content blocks
525
+ */
526
+ private extractAllSystemReminderContents(content: string): string[] {
527
+ const results: string[] = [];
528
+ const regex = /<system-reminder>\n?([\s\S]*?)\n?<\/system-reminder>/g;
529
+ let match: RegExpExecArray | null;
530
+ let lastIndex = 0;
531
+
532
+ while ((match = regex.exec(content)) !== null) {
533
+ // Check for text before this match (after the last match)
534
+ const beforeText = content.substring(lastIndex, match.index).trim();
535
+ if (beforeText) {
536
+ results.push(beforeText);
537
+ }
538
+
539
+ // Add the inner content of this system-reminder block
540
+ const innerContent = match[1].trim();
541
+ if (innerContent) {
542
+ results.push(innerContent);
543
+ }
544
+
545
+ lastIndex = regex.lastIndex;
546
+ }
547
+
548
+ // Check for text after the last match
549
+ const afterText = content.substring(lastIndex).trim();
550
+ if (afterText) {
551
+ results.push(afterText);
552
+ }
553
+
554
+ return results;
555
+ }
556
+
557
+ /**
558
+ * Apply reactive compression if needed.
559
+ * This ensures conversation history fits within the token budget.
560
+ */
561
+ private async applyCompression(context: MessageCompilerContext): Promise<void> {
562
+ // Skip compression if LLMService not available
563
+ if (!this.llmService) {
564
+ return;
565
+ }
566
+
567
+ const compressionService = createCompressionService(
568
+ this.conversationStore,
569
+ this.llmService!,
570
+ this.compressionLlmService
571
+ );
572
+
573
+ // Get compression config with proper defaults (enabled: true by default)
574
+ // CRITICAL: Don't check config.compression?.enabled directly - it bypasses
575
+ // CompressionService defaults. Let CompressionService handle the enabled check.
576
+ const compressionConfig = compressionService.getCompressionConfig();
577
+ if (!compressionConfig.enabled) {
578
+ return;
579
+ }
580
+
581
+ // Apply reactive compression (sub-span removed - CompressionService has its own span when work is done)
582
+ try {
583
+ const tokenBudget = compressionConfig.tokenBudget;
584
+ await compressionService.ensureUnderLimit(
585
+ context.conversation.id,
586
+ tokenBudget
587
+ );
588
+ } catch (error) {
589
+ // Non-critical - log and continue without compression
590
+ logger.warn("Reactive compression failed", {
591
+ conversationId: context.conversation.id,
592
+ ralNumber: context.ralNumber,
593
+ error: error instanceof Error ? error.message : String(error),
594
+ });
595
+ }
596
+ }
597
+ }