@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,93 @@
1
+ import { getTrustPubkeyService, type TrustResult } from "@/services/trust-pubkeys";
2
+ import { logger } from "@/utils/logger";
3
+ import { trace } from "@opentelemetry/api";
4
+ import type { NDKEvent } from "@nostr-dev-kit/ndk";
5
+
6
+ /**
7
+ * PubkeyGateService gates incoming events based on pubkey authorization.
8
+ *
9
+ * An event is allowed through if its author's pubkey is trusted by TrustPubkeyService
10
+ * (whitelisted, backend, or known agent). All other events are silently dropped.
11
+ *
12
+ * Design principles:
13
+ * - Fail-closed: if the trust check errors, the event is denied
14
+ * - Sanitized logging: only pubkey prefix + event kind for observability
15
+ * - OpenTelemetry telemetry for audit trail
16
+ * - Sync trust checks for performance (requires backend pubkey cache initialization)
17
+ */
18
+ export class PubkeyGateService {
19
+ private static instance: PubkeyGateService;
20
+
21
+ private constructor() {}
22
+
23
+ static getInstance(): PubkeyGateService {
24
+ if (!PubkeyGateService.instance) {
25
+ PubkeyGateService.instance = new PubkeyGateService();
26
+ }
27
+ return PubkeyGateService.instance;
28
+ }
29
+
30
+ /**
31
+ * Check if an incoming event should be allowed through the gate.
32
+ *
33
+ * Uses synchronous trust checks for performance. The backend pubkey cache
34
+ * must be initialized via TrustPubkeyService.initializeBackendPubkeyCache()
35
+ * before calling this method for accurate backend pubkey checks.
36
+ *
37
+ * @param event The incoming NDKEvent to check
38
+ * @returns true if the event should be routed, false if it should be dropped
39
+ */
40
+ shouldAllowEvent(event: NDKEvent): boolean {
41
+ const pubkey = event.pubkey;
42
+
43
+ if (!pubkey) {
44
+ logger.debug("[PUBKEY_GATE] Event denied: missing pubkey", {
45
+ kind: event.kind,
46
+ });
47
+ this.recordDenied(event, "no_pubkey");
48
+ return false;
49
+ }
50
+
51
+ let trustResult: TrustResult;
52
+ try {
53
+ trustResult = getTrustPubkeyService().isTrustedEventSync(event);
54
+ } catch (error) {
55
+ // Fail-closed: if the trust check errors, deny
56
+ logger.warn("[PUBKEY_GATE] Trust check failed, denying event (fail-closed)", {
57
+ pubkey: pubkey.substring(0, 8),
58
+ kind: event.kind,
59
+ error: error instanceof Error ? error.message : String(error),
60
+ });
61
+ this.recordDenied(event, "error");
62
+ return false;
63
+ }
64
+
65
+ if (!trustResult.trusted) {
66
+ logger.debug("[PUBKEY_GATE] Event denied: untrusted pubkey", {
67
+ pubkey: pubkey.substring(0, 8),
68
+ kind: event.kind,
69
+ });
70
+ this.recordDenied(event, "untrusted");
71
+ return false;
72
+ }
73
+
74
+ return true;
75
+ }
76
+
77
+ /**
78
+ * Record a denied event in telemetry for audit trail.
79
+ */
80
+ private recordDenied(event: NDKEvent, reason: string): void {
81
+ trace.getActiveSpan()?.addEvent("pubkey_gate.denied", {
82
+ "gate.pubkey": event.pubkey?.substring(0, 8) ?? "unknown",
83
+ "gate.kind": event.kind ?? 0,
84
+ "gate.reason": reason,
85
+ });
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Get the PubkeyGateService singleton instance
91
+ */
92
+ export const getPubkeyGateService = (): PubkeyGateService =>
93
+ PubkeyGateService.getInstance();
@@ -0,0 +1 @@
1
+ export { PubkeyGateService, getPubkeyGateService } from "./PubkeyGateService";
@@ -0,0 +1,292 @@
1
+ import * as path from "node:path";
2
+ import { fileExists, readJsonFile } from "@/lib/fs";
3
+ import { config } from "@/services/ConfigService";
4
+ import { resolveApiKey } from "@/services/config/types";
5
+ import { logger } from "@/utils/logger";
6
+ import {
7
+ type EmbeddingProvider,
8
+ LocalTransformerEmbeddingProvider,
9
+ OpenAIEmbeddingProvider,
10
+ } from "@/services/embedding";
11
+
12
+ /**
13
+ * Configuration for embedding providers
14
+ */
15
+ export interface EmbeddingConfig {
16
+ provider: string; // "local" or any configured provider ID (openai, openrouter, etc.)
17
+ model: string;
18
+ apiKey?: string;
19
+ baseUrl?: string;
20
+ }
21
+
22
+ export interface EmbeddingConfigOptions {
23
+ /**
24
+ * Path to the project metadata directory (e.g., ~/.tenex/projects/<dTag>)
25
+ */
26
+ metadataPath?: string;
27
+ /**
28
+ * Path to the project repository root (used to derive project .tenex path)
29
+ */
30
+ projectPath?: string;
31
+ /**
32
+ * Scope for resolution. "auto" (default) tries project then global.
33
+ * "project" prioritizes project config but will still fall back to global defaults.
34
+ * "global" only reads the global config.
35
+ */
36
+ scope?: "auto" | "project" | "global";
37
+ }
38
+
39
+ /**
40
+ * Raw configuration as it may appear in JSON files
41
+ * Supports both old format (string or partial object) and new format (full object)
42
+ */
43
+ type RawEmbeddingConfig =
44
+ | string // Old format: just model name
45
+ | { model: string; provider?: never } // Old format: object with just model
46
+ | { provider: string; model: string; apiKey?: string; baseUrl?: string }; // New format
47
+
48
+ function isRawEmbeddingConfig(value: unknown): value is RawEmbeddingConfig {
49
+ if (typeof value === "string") {
50
+ return true;
51
+ }
52
+
53
+ if (typeof value !== "object" || value === null) {
54
+ return false;
55
+ }
56
+
57
+ const obj = value as Record<string, unknown>;
58
+
59
+ // Must have a model
60
+ if (!("model" in obj) || typeof obj.model !== "string") {
61
+ return false;
62
+ }
63
+
64
+ // If provider is present, must be a string
65
+ if ("provider" in obj && typeof obj.provider !== "string") {
66
+ return false;
67
+ }
68
+
69
+ // If apiKey is present, must be string
70
+ if ("apiKey" in obj && typeof obj.apiKey !== "string") {
71
+ return false;
72
+ }
73
+
74
+ // If baseUrl is present, must be string
75
+ if ("baseUrl" in obj && typeof obj.baseUrl !== "string") {
76
+ return false;
77
+ }
78
+
79
+ return true;
80
+ }
81
+
82
+ /**
83
+ * Factory for creating embedding providers based on configuration
84
+ */
85
+ export class EmbeddingProviderFactory {
86
+ private static readonly EMBED_CONFIG_FILE = "embed.json";
87
+ private static readonly DEFAULT_CONFIG: EmbeddingConfig = {
88
+ provider: "local",
89
+ model: "Xenova/all-MiniLM-L6-v2",
90
+ };
91
+
92
+ /**
93
+ * Base URLs for known OpenAI-compatible providers
94
+ */
95
+ private static readonly PROVIDER_BASE_URLS: Record<string, string> = {
96
+ openai: "https://api.openai.com/v1",
97
+ openrouter: "https://openrouter.ai/api/v1",
98
+ };
99
+
100
+ /**
101
+ * Create an embedding provider based on configuration
102
+ */
103
+ static async create(
104
+ customConfig?: EmbeddingConfig,
105
+ options?: EmbeddingConfigOptions
106
+ ): Promise<EmbeddingProvider> {
107
+ const embeddingConfig = customConfig || (await EmbeddingProviderFactory.loadConfiguration(options));
108
+
109
+ logger.debug(`Creating embedding provider: ${embeddingConfig.provider}/${embeddingConfig.model}`);
110
+
111
+ // Local provider
112
+ if (embeddingConfig.provider === "local") {
113
+ return new LocalTransformerEmbeddingProvider(embeddingConfig.model);
114
+ }
115
+
116
+ // All other providers are treated as OpenAI-compatible
117
+ if (!embeddingConfig.apiKey) {
118
+ throw new Error(
119
+ `API key required for ${embeddingConfig.provider}. Configure with 'tenex setup embed'.`
120
+ );
121
+ }
122
+
123
+ const baseUrl = embeddingConfig.baseUrl ||
124
+ EmbeddingProviderFactory.PROVIDER_BASE_URLS[embeddingConfig.provider] ||
125
+ "https://api.openai.com/v1";
126
+
127
+ return new OpenAIEmbeddingProvider(embeddingConfig.apiKey, embeddingConfig.model, baseUrl);
128
+ }
129
+
130
+ /**
131
+ * Load embedding configuration using configured paths.
132
+ * If a project path is provided, project config is tried first, then global.
133
+ * If no paths are provided, falls back to CWD project path (legacy) and then global.
134
+ */
135
+ static async loadConfiguration(options?: EmbeddingConfigOptions): Promise<EmbeddingConfig> {
136
+ try {
137
+ const basePaths = EmbeddingProviderFactory.resolveConfigBases(options);
138
+
139
+ for (const basePath of basePaths) {
140
+ const configPath = path.join(basePath, EmbeddingProviderFactory.EMBED_CONFIG_FILE);
141
+ if (!(await fileExists(configPath))) continue;
142
+
143
+ const rawConfig = await readJsonFile<unknown>(configPath);
144
+ if (!isRawEmbeddingConfig(rawConfig)) {
145
+ logger.warn(`Invalid embedding config at ${configPath}, using defaults`);
146
+ return EmbeddingProviderFactory.DEFAULT_CONFIG;
147
+ }
148
+
149
+ logger.debug(`Loaded embedding config from ${configPath}`);
150
+ return await EmbeddingProviderFactory.parseConfig(rawConfig);
151
+ }
152
+
153
+ logger.debug("No embedding configuration found, using defaults");
154
+ return EmbeddingProviderFactory.DEFAULT_CONFIG;
155
+ } catch (error) {
156
+ logger.warn("Failed to load embedding configuration, using defaults", { error });
157
+ return EmbeddingProviderFactory.DEFAULT_CONFIG;
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Load provider credentials from providers.json
163
+ */
164
+ private static async loadProviderCredentials(providerId: string): Promise<{ apiKey?: string; baseUrl?: string }> {
165
+ try {
166
+ const globalPath = config.getGlobalPath();
167
+ const providersConfig = await config.loadTenexProviders(globalPath);
168
+ const providerCreds = providersConfig.providers[providerId];
169
+ return {
170
+ apiKey: resolveApiKey(providerCreds?.apiKey),
171
+ baseUrl: providerCreds?.baseUrl,
172
+ };
173
+ } catch (error) {
174
+ logger.debug(`Failed to load provider credentials for ${providerId}`, { error });
175
+ return {};
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Parse and validate embedding configuration
181
+ */
182
+ private static async parseConfig(raw: RawEmbeddingConfig): Promise<EmbeddingConfig> {
183
+ // Support both old format (just model string) and new format
184
+ if (typeof raw === "string" || (raw?.model && !raw.provider)) {
185
+ // Old format or just model specified
186
+ const modelId = typeof raw === "string" ? raw : raw.model;
187
+
188
+ // Infer provider from model name
189
+ if (modelId.includes("text-embedding")) {
190
+ const creds = await EmbeddingProviderFactory.loadProviderCredentials("openai");
191
+ return {
192
+ provider: "openai",
193
+ model: modelId,
194
+ apiKey: creds.apiKey,
195
+ baseUrl: creds.baseUrl,
196
+ };
197
+ }
198
+
199
+ return {
200
+ provider: "local",
201
+ model: modelId,
202
+ };
203
+ }
204
+
205
+ // New format with explicit provider
206
+ const rawConfig = raw as { provider?: string; model?: string; apiKey?: string; baseUrl?: string };
207
+ const provider = rawConfig.provider || "local";
208
+
209
+ // Load credentials from providers.json if not provided inline
210
+ let apiKey = rawConfig.apiKey;
211
+ let baseUrl = rawConfig.baseUrl;
212
+
213
+ if (provider !== "local" && !apiKey) {
214
+ const creds = await EmbeddingProviderFactory.loadProviderCredentials(provider);
215
+ apiKey = creds.apiKey;
216
+ baseUrl = baseUrl || creds.baseUrl;
217
+ }
218
+
219
+ return {
220
+ provider,
221
+ model: rawConfig.model || EmbeddingProviderFactory.DEFAULT_CONFIG.model,
222
+ apiKey,
223
+ baseUrl,
224
+ };
225
+ }
226
+
227
+ /**
228
+ * Save embedding configuration
229
+ */
230
+ static async saveConfiguration(
231
+ embeddingConfig: EmbeddingConfig,
232
+ scope: "global" | "project" = "global",
233
+ options?: EmbeddingConfigOptions
234
+ ): Promise<void> {
235
+ const basePath =
236
+ scope === "global"
237
+ ? config.getGlobalPath()
238
+ : EmbeddingProviderFactory.resolveProjectBase(options) ||
239
+ config.getProjectPath(process.cwd());
240
+
241
+ const configPath = path.join(basePath, EmbeddingProviderFactory.EMBED_CONFIG_FILE);
242
+
243
+ // Don't save API key to file - it's stored in providers.json
244
+ const configToSave: { provider: string; model: string; baseUrl?: string } = {
245
+ provider: embeddingConfig.provider,
246
+ model: embeddingConfig.model,
247
+ };
248
+
249
+ // Only save baseUrl if it's a custom one (not the default for the provider)
250
+ const defaultBaseUrl = EmbeddingProviderFactory.PROVIDER_BASE_URLS[embeddingConfig.provider];
251
+ if (embeddingConfig.baseUrl && embeddingConfig.baseUrl !== defaultBaseUrl) {
252
+ configToSave.baseUrl = embeddingConfig.baseUrl;
253
+ }
254
+
255
+ const { writeJsonFile, ensureDirectory } = await import("@/lib/fs");
256
+ await ensureDirectory(basePath);
257
+ await writeJsonFile(configPath, configToSave);
258
+
259
+ logger.info(
260
+ `Embedding configuration saved to ${scope} config: ${embeddingConfig.provider}/${embeddingConfig.model}`
261
+ );
262
+ }
263
+
264
+ private static resolveConfigBases(options?: EmbeddingConfigOptions): string[] {
265
+ const scope = options?.scope ?? "auto";
266
+ const bases: string[] = [];
267
+
268
+ if (scope !== "global") {
269
+ const projectBase = EmbeddingProviderFactory.resolveProjectBase(options);
270
+ if (projectBase) {
271
+ bases.push(projectBase);
272
+ } else if (!options) {
273
+ // Legacy fallback to current working directory
274
+ bases.push(config.getProjectPath(process.cwd()));
275
+ }
276
+ }
277
+
278
+ // Always append global as fallback/default
279
+ bases.push(config.getGlobalPath());
280
+ return bases;
281
+ }
282
+
283
+ private static resolveProjectBase(options?: EmbeddingConfigOptions): string | undefined {
284
+ if (options?.metadataPath) {
285
+ return options.metadataPath;
286
+ }
287
+ if (options?.projectPath) {
288
+ return config.getProjectPath(options.projectPath);
289
+ }
290
+ return undefined;
291
+ }
292
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * LanceDBMaintenanceService - Periodic compaction and cleanup
3
+ *
4
+ * Runs optimize() on all LanceDB tables to:
5
+ * - Compact small fragment files into larger ones
6
+ * - Prune old version manifests
7
+ *
8
+ * This prevents fragmentation accumulation from frequent writes.
9
+ * Uses a self-scheduling setTimeout pattern (like ConversationIndexingJob)
10
+ * to prevent overlapping runs.
11
+ */
12
+
13
+ import { logger } from "@/utils/logger";
14
+ import { RAGDatabaseService } from "./RAGDatabaseService";
15
+
16
+ /** Default interval: 2 hours (in milliseconds) */
17
+ const DEFAULT_INTERVAL_MS = 2 * 60 * 60 * 1000;
18
+
19
+ /**
20
+ * Cleanup versions older than this duration.
21
+ * We keep 1 day of history for safety; the current version is never removed.
22
+ */
23
+ const DEFAULT_CLEANUP_OLDER_THAN_DAYS = 1;
24
+
25
+ /**
26
+ * Periodic LanceDB maintenance service
27
+ */
28
+ export class LanceDBMaintenanceService {
29
+ private static instance: LanceDBMaintenanceService | null = null;
30
+ private timer: NodeJS.Timeout | null = null;
31
+ private isRunning = false;
32
+ private isMaintenanceRunning = false;
33
+ private intervalMs: number;
34
+
35
+ private constructor(intervalMs: number = DEFAULT_INTERVAL_MS) {
36
+ this.intervalMs = intervalMs;
37
+ }
38
+
39
+ /**
40
+ * Get singleton instance
41
+ */
42
+ public static getInstance(intervalMs?: number): LanceDBMaintenanceService {
43
+ if (!LanceDBMaintenanceService.instance) {
44
+ LanceDBMaintenanceService.instance = new LanceDBMaintenanceService(intervalMs);
45
+ }
46
+ return LanceDBMaintenanceService.instance;
47
+ }
48
+
49
+ /**
50
+ * Start the periodic maintenance job
51
+ */
52
+ public start(): void {
53
+ if (this.isRunning) {
54
+ logger.warn("LanceDBMaintenanceService is already running");
55
+ return;
56
+ }
57
+
58
+ this.isRunning = true;
59
+ logger.info("Starting LanceDBMaintenanceService", {
60
+ intervalMs: this.intervalMs,
61
+ intervalHours: this.intervalMs / (60 * 60 * 1000),
62
+ });
63
+
64
+ // Run first maintenance after a short delay (30 seconds)
65
+ // to let the system finish booting
66
+ this.scheduleNextRun(30_000);
67
+ }
68
+
69
+ /**
70
+ * Schedule the next maintenance run (self-scheduling to prevent overlaps)
71
+ */
72
+ private scheduleNextRun(delayMs: number): void {
73
+ if (!this.isRunning) return;
74
+
75
+ this.timer = setTimeout(async () => {
76
+ try {
77
+ await this.runMaintenance();
78
+ } catch (error) {
79
+ logger.error("LanceDB maintenance failed", { error });
80
+ } finally {
81
+ // Schedule next run only after this one completes
82
+ this.scheduleNextRun(this.intervalMs);
83
+ }
84
+ }, delayMs);
85
+ }
86
+
87
+ /**
88
+ * Stop the periodic maintenance job
89
+ */
90
+ public stop(): void {
91
+ if (!this.isRunning) {
92
+ return;
93
+ }
94
+
95
+ if (this.timer) {
96
+ clearTimeout(this.timer);
97
+ this.timer = null;
98
+ }
99
+
100
+ this.isRunning = false;
101
+ logger.info("LanceDBMaintenanceService stopped");
102
+ }
103
+
104
+ /**
105
+ * Run maintenance on all LanceDB tables
106
+ */
107
+ private async runMaintenance(): Promise<void> {
108
+ if (this.isMaintenanceRunning) {
109
+ logger.warn("Skipping LanceDB maintenance - previous run still in progress");
110
+ return;
111
+ }
112
+
113
+ this.isMaintenanceRunning = true;
114
+
115
+ // Use a temporary DB service to avoid coupling to RAGService singleton
116
+ // (which requires embedding provider initialization)
117
+ let dbService: RAGDatabaseService | null = null;
118
+
119
+ try {
120
+ dbService = new RAGDatabaseService();
121
+ const tableNames = await dbService.listTables();
122
+
123
+ if (tableNames.length === 0) {
124
+ logger.debug("No LanceDB tables to optimize");
125
+ return;
126
+ }
127
+
128
+ logger.info("Starting LanceDB maintenance", { tableCount: tableNames.length });
129
+
130
+ const cleanupOlderThan = new Date();
131
+ cleanupOlderThan.setDate(cleanupOlderThan.getDate() - DEFAULT_CLEANUP_OLDER_THAN_DAYS);
132
+
133
+ let optimizedCount = 0;
134
+
135
+ for (const tableName of tableNames) {
136
+ try {
137
+ const table = await dbService.getTable(tableName);
138
+ const stats = await table.optimize({
139
+ cleanupOlderThan,
140
+ });
141
+
142
+ optimizedCount++;
143
+ logger.info(`Optimized table '${tableName}'`, {
144
+ compaction: stats.compaction,
145
+ prune: stats.prune,
146
+ });
147
+ } catch (error) {
148
+ const message = error instanceof Error ? error.message : String(error);
149
+ logger.error(`Failed to optimize table '${tableName}'`, {
150
+ error: message,
151
+ });
152
+ // Continue with other tables
153
+ }
154
+ }
155
+
156
+ logger.info("LanceDB maintenance complete", {
157
+ tablesOptimized: optimizedCount,
158
+ totalTables: tableNames.length,
159
+ });
160
+ } catch (error) {
161
+ const message = error instanceof Error ? error.message : String(error);
162
+ logger.error("LanceDB maintenance failed", { error: message });
163
+ } finally {
164
+ if (dbService) {
165
+ try {
166
+ await dbService.close();
167
+ } catch (closeError) {
168
+ logger.debug("Failed to close maintenance DB service", { error: closeError });
169
+ }
170
+ }
171
+ this.isMaintenanceRunning = false;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Force an immediate maintenance run (for manual triggering)
177
+ */
178
+ public async forceRun(): Promise<void> {
179
+ await this.runMaintenance();
180
+ }
181
+
182
+ /**
183
+ * Get current service status
184
+ */
185
+ public getStatus(): {
186
+ isRunning: boolean;
187
+ isMaintenanceRunning: boolean;
188
+ intervalMs: number;
189
+ } {
190
+ return {
191
+ isRunning: this.isRunning,
192
+ isMaintenanceRunning: this.isMaintenanceRunning,
193
+ intervalMs: this.intervalMs,
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Reset singleton instance (for testing)
199
+ */
200
+ public static resetInstance(): void {
201
+ if (LanceDBMaintenanceService.instance) {
202
+ LanceDBMaintenanceService.instance.stop();
203
+ LanceDBMaintenanceService.instance = null;
204
+ }
205
+ }
206
+ }
207
+
208
+ // Export lazy getter
209
+ export function getLanceDBMaintenanceService(): LanceDBMaintenanceService {
210
+ return LanceDBMaintenanceService.getInstance();
211
+ }