@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,181 @@
1
+ import type { AgentInstance } from "@/agents/types";
2
+ import type { ConversationStore } from "@/conversations/ConversationStore";
3
+ import { AgentEventDecoder } from "@/nostr/AgentEventDecoder";
4
+ import type { ProjectContext } from "@/services/projects";
5
+ import { logger } from "@/utils/logger";
6
+ import type { NDKEvent } from "@nostr-dev-kit/ndk";
7
+ import chalk from "chalk";
8
+ import type { DelegationCompletionResult } from "./DelegationCompletionHandler";
9
+
10
+ /**
11
+ * AgentRouter is a static utility class that determines which agent
12
+ * should handle an incoming event. This centralizes the routing logic
13
+ * that was previously embedded in reply.ts.
14
+ */
15
+
16
+ // biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
17
+ export class AgentRouter {
18
+ /**
19
+ * Process a stop signal (kind 24134) to block an agent in a conversation.
20
+ * Returns { blocked: true } if the agent was blocked.
21
+ */
22
+ static processStopSignal(
23
+ event: NDKEvent,
24
+ conversation: ConversationStore,
25
+ projectContext: ProjectContext
26
+ ): { blocked: boolean } {
27
+ const pTags = event.getMatchingTags("p");
28
+
29
+ for (const [, agentPubkey] of pTags) {
30
+ const agent = projectContext.getAgentByPubkey(agentPubkey);
31
+ if (agent) {
32
+ conversation.blockAgent(agentPubkey);
33
+ logger.info(
34
+ chalk.yellow(
35
+ `Blocked agent ${agent.slug} in conversation ${conversation.id.substring(0, 8)}`
36
+ )
37
+ );
38
+ }
39
+ }
40
+
41
+ return { blocked: pTags.length > 0 };
42
+ }
43
+
44
+ /**
45
+ * Determine which agents should handle the event based on p-tags,
46
+ * event author, and other context.
47
+ *
48
+ * @param event - The incoming event
49
+ * @param projectContext - Project context with agent information
50
+ * @param conversation - Optional conversation to check for blocked agents
51
+ * @returns Array of target agents that should process this event
52
+ */
53
+ static resolveTargetAgents(
54
+ event: NDKEvent,
55
+ projectContext: ProjectContext,
56
+ conversation?: ConversationStore
57
+ ): AgentInstance[] {
58
+ const mentionedPubkeys = AgentEventDecoder.getMentionedPubkeys(event);
59
+
60
+ // Check if the event author is an agent in the system
61
+ const isAuthorAnAgent = AgentEventDecoder.isEventFromAgent(event, projectContext.agents);
62
+
63
+ // Check for p-tagged agents regardless of sender
64
+ if (mentionedPubkeys.length > 0) {
65
+ // Find ALL p-tagged system agents
66
+ const targetAgents: AgentInstance[] = [];
67
+ for (const pubkey of mentionedPubkeys) {
68
+ // Skip blocked agents
69
+ if (conversation?.isAgentBlocked(pubkey)) {
70
+ const agent = projectContext.getAgentByPubkey(pubkey);
71
+ logger.info(
72
+ chalk.yellow(
73
+ `Skipping blocked agent ${agent?.slug ?? pubkey.substring(0, 8)} in conversation ${conversation.id.substring(0, 8)}`
74
+ )
75
+ );
76
+ continue;
77
+ }
78
+
79
+ const agent = projectContext.getAgentByPubkey(pubkey);
80
+ if (agent) {
81
+ targetAgents.push(agent);
82
+ }
83
+ }
84
+
85
+ if (targetAgents.length > 0) {
86
+ const agentNames = targetAgents.map((a) => a.name).join(", ");
87
+ logger.info(
88
+ chalk.gray(`Routing to ${targetAgents.length} p-tagged agent(s): ${agentNames}`)
89
+ );
90
+ return targetAgents;
91
+ }
92
+ }
93
+
94
+ // If no p-tags, don't route to anyone - just log it
95
+ if (mentionedPubkeys.length === 0) {
96
+ const senderType = isAuthorAnAgent ? "agent" : "user";
97
+ logger.info(
98
+ chalk.gray(
99
+ `Event from ${senderType} ${event.pubkey.substring(0, 8)} without p-tags - not routing to any agent`
100
+ )
101
+ );
102
+ return [];
103
+ }
104
+
105
+ return [];
106
+ }
107
+
108
+ /**
109
+ * Unblock an agent in a conversation if the sender is whitelisted.
110
+ * Returns { unblocked: true } if successful.
111
+ */
112
+ static unblockAgent(
113
+ event: NDKEvent,
114
+ conversation: ConversationStore,
115
+ projectContext: ProjectContext,
116
+ whitelist: Set<string>
117
+ ): { unblocked: boolean } {
118
+ // Only whitelisted pubkeys can unblock
119
+ if (!whitelist.has(event.pubkey)) {
120
+ return { unblocked: false };
121
+ }
122
+
123
+ const pTags = event.getMatchingTags("p");
124
+ let unblocked = false;
125
+
126
+ for (const [, agentPubkey] of pTags) {
127
+ if (conversation.isAgentBlocked(agentPubkey)) {
128
+ conversation.unblockAgent(agentPubkey);
129
+ const agent = projectContext.getAgentByPubkey(agentPubkey);
130
+ logger.info(
131
+ chalk.green(
132
+ `Unblocked agent ${agent?.slug ?? agentPubkey.substring(0, 8)} in conversation ${conversation.id.substring(0, 8)} by ${event.pubkey.substring(0, 8)}`
133
+ )
134
+ );
135
+ unblocked = true;
136
+ }
137
+ }
138
+
139
+ return { unblocked };
140
+ }
141
+
142
+ /**
143
+ * Resolve routing target for a delegation completion.
144
+ * Returns the agent and conversation ID where the waiting RAL lives,
145
+ * or null if the delegation wasn't recorded or agent not found.
146
+ */
147
+ static resolveDelegationTarget(
148
+ delegationResult: DelegationCompletionResult,
149
+ projectContext: ProjectContext
150
+ ): { agent: AgentInstance; conversationId: string } | null {
151
+ if (!delegationResult.recorded) {
152
+ return null;
153
+ }
154
+
155
+ const { agentSlug, conversationId } = delegationResult;
156
+ if (!agentSlug || !conversationId) {
157
+ logger.warn(
158
+ chalk.yellow(
159
+ "[AgentRouter] Delegation recorded but missing agentSlug or conversationId"
160
+ )
161
+ );
162
+ return null;
163
+ }
164
+
165
+ const waitingAgent = projectContext.getAgent(agentSlug);
166
+ if (!waitingAgent) {
167
+ logger.warn(
168
+ chalk.yellow(`[AgentRouter] Waiting agent not found: ${agentSlug}`)
169
+ );
170
+ return null;
171
+ }
172
+
173
+ logger.info(
174
+ chalk.gray(
175
+ `Routing delegation completion to ${agentSlug} in conversation ${conversationId.substring(0, 8)}`
176
+ )
177
+ );
178
+
179
+ return { agent: waitingAgent, conversationId };
180
+ }
181
+ }
@@ -0,0 +1,232 @@
1
+ import { TagExtractor } from "@/nostr/TagExtractor";
2
+ import { getProjectContext } from "@/services/projects";
3
+ import { logger } from "@/utils/logger";
4
+ import { shortenConversationId } from "@/utils/conversation-id";
5
+ import type { NDKEvent } from "@nostr-dev-kit/ndk";
6
+ import { RALRegistry } from "@/services/ral";
7
+ import { ConversationStore } from "@/conversations/ConversationStore";
8
+ import { trace, SpanStatusCode, context as otelContext } from "@opentelemetry/api";
9
+
10
+ const tracer = trace.getTracer("tenex.delegation");
11
+
12
+ export interface DelegationCompletionResult {
13
+ /** Whether a completion was recorded */
14
+ recorded: boolean;
15
+ /** The agent slug that's waiting for this delegation (if any) */
16
+ agentSlug?: string;
17
+ /** The conversation ID where the delegation was made */
18
+ conversationId?: string;
19
+ /** Number of pending delegations remaining */
20
+ pendingCount?: number;
21
+ }
22
+
23
+ /**
24
+ * Record a delegation completion event in RALRegistry.
25
+ * Does NOT handle routing or resumption - that's handled by normal routing + AgentExecutor.
26
+ *
27
+ * Flow:
28
+ * 1. Find which delegation this responds to (via e-tag)
29
+ * 2. Look up which RAL is waiting for this delegation
30
+ * 3. Record the completion
31
+ */
32
+ export async function handleDelegationCompletion(
33
+ event: NDKEvent
34
+ ): Promise<DelegationCompletionResult> {
35
+ // Early exit: check for e-tags BEFORE creating a span to avoid trace noise
36
+ // Events without e-tags cannot be delegation completions
37
+ const eTags = TagExtractor.getETags(event);
38
+ if (eTags.length === 0) {
39
+ return { recorded: false };
40
+ }
41
+
42
+ const span = tracer.startSpan("tenex.delegation.completion_check", {
43
+ attributes: {
44
+ "event.id": event.id || "",
45
+ "event.pubkey": event.pubkey,
46
+ "event.kind": event.kind || 0,
47
+ "delegation.etag_count": eTags.length,
48
+ },
49
+ }, otelContext.active());
50
+
51
+ try {
52
+ const ralRegistry = RALRegistry.getInstance();
53
+
54
+ // Try e-tags in reverse order (last to first) as per NIP-10 convention
55
+ // The last e-tag is typically the direct reply target in threaded conversations
56
+ let location = null;
57
+ let delegationEventId = null;
58
+
59
+ for (let i = eTags.length - 1; i >= 0; i--) {
60
+ const eTag = eTags[i];
61
+ span.addEvent("trying_delegation_etag", {
62
+ "delegation.event_id": eTag,
63
+ "etag.index": i,
64
+ });
65
+
66
+ // Look up the pending delegation to get context for validation
67
+ const pendingInfo = ralRegistry.findDelegation(eTag);
68
+
69
+ if (!pendingInfo?.pending) {
70
+ // No pending delegation found for this e-tag, try next one
71
+ span.addEvent("no_pending_delegation", {
72
+ "delegation.event_id": eTag,
73
+ });
74
+ continue;
75
+ }
76
+
77
+ // Validate that the event author is the delegated agent
78
+ // This prevents OTHER agents from falsely completing delegations
79
+ if (event.pubkey !== pendingInfo.pending.recipientPubkey) {
80
+ span.addEvent("completion_sender_mismatch", {
81
+ "delegation.event_id": eTag,
82
+ "expected.recipient_pubkey": pendingInfo.pending.recipientPubkey.substring(0, 12),
83
+ "actual.sender_pubkey": event.pubkey.substring(0, 12),
84
+ "validation.matched": false,
85
+ });
86
+ logger.debug("[handleDelegationCompletion] Ignoring event - sender is not the delegated agent", {
87
+ delegationEventId: eTag.substring(0, 8),
88
+ expectedRecipient: pendingInfo.pending.recipientPubkey.substring(0, 12),
89
+ actualSender: event.pubkey.substring(0, 12),
90
+ });
91
+ continue; // Skip to next e-tag
92
+ }
93
+
94
+ span.addEvent("completion_sender_validated", {
95
+ "delegation.event_id": eTag,
96
+ "expected.recipient_pubkey": pendingInfo.pending.recipientPubkey.substring(0, 12),
97
+ "actual.sender_pubkey": event.pubkey.substring(0, 12),
98
+ "validation.matched": true,
99
+ });
100
+
101
+ // DEFENSE-IN-DEPTH: Early exit for killed delegations.
102
+ // The authoritative check is in RALRegistry.recordCompletion(), but we check
103
+ // here too using the isDelegationKilled() helper to avoid unnecessary work.
104
+ if (ralRegistry.isDelegationKilled(eTag)) {
105
+ span.addEvent("completion_skipped_delegation_killed", {
106
+ "delegation.event_id": eTag,
107
+ "delegation.killed_at": pendingInfo.pending.killedAt,
108
+ });
109
+ logger.info("[handleDelegationCompletion] Ignoring completion - delegation was killed", {
110
+ delegationEventId: eTag.substring(0, 8),
111
+ killedAt: pendingInfo.pending.killedAt,
112
+ completionEventId: event.id?.substring(0, 8),
113
+ });
114
+ continue; // Skip to next e-tag - this delegation was killed
115
+ }
116
+
117
+ // Record the completion (looks up RAL internally via delegation conversation ID)
118
+ // NOTE: We no longer build/pass fullTranscript here. The marker-based system
119
+ // (RALResolver + MessageBuilder) reads the conversation transcript directly from
120
+ // ConversationStore when expanding delegation markers. Storing redundant transcripts
121
+ // in CompletedDelegation was causing unnecessary memory/disk bloat.
122
+ const result = ralRegistry.recordCompletion({
123
+ delegationConversationId: eTag,
124
+ recipientPubkey: event.pubkey,
125
+ response: event.content,
126
+ completedAt: Math.floor(Date.now() / 1000), // Use seconds to match pending markers
127
+ });
128
+
129
+ if (result) {
130
+ location = result;
131
+ delegationEventId = eTag;
132
+
133
+ // Add the completion event to the delegation conversation store.
134
+ // This ensures getDelegationMessages() can return the user's response
135
+ // when building the delegation marker transcript. Without this, ask
136
+ // conversations would show "transcript unavailable" instead of the
137
+ // actual user response.
138
+ // See: naddr1qvzqqqr4gupzqkmm302xww6uyne99rnhl5kjj53wthjypm2qaem9uz9fdf3hzcf0qyghwumn8ghj7ar9dejhstnrdpshgtcq9p382emxd9uz6en0d3kx7am4wqkkjmn2v43hg6t0dckhzat9w4jj6cmvv4shy6twvullqw7x
139
+ const delegationStore = ConversationStore.get(eTag);
140
+ if (delegationStore) {
141
+ try {
142
+ await ConversationStore.addEvent(eTag, event);
143
+ span.addEvent("completion_event_added_to_delegation_store", {
144
+ "delegation.conversation_id": shortenConversationId(eTag),
145
+ });
146
+ } catch (addEventError) {
147
+ // Don't throw after recordCompletion has already run.
148
+ // The completion is recorded - only transcript storage failed.
149
+ logger.warn("[handleDelegationCompletion] Failed to add completion event to delegation store", {
150
+ delegationEventId: eTag.substring(0, 8),
151
+ completionEventId: event.id?.substring(0, 8),
152
+ error: addEventError instanceof Error ? addEventError.message : String(addEventError),
153
+ });
154
+ span.addEvent("completion_event_add_failed", {
155
+ "delegation.conversation_id": shortenConversationId(eTag),
156
+ "error": addEventError instanceof Error ? addEventError.message : String(addEventError),
157
+ });
158
+ }
159
+ }
160
+
161
+ break; // Found a matching delegation
162
+ }
163
+ }
164
+
165
+ if (!location) {
166
+ span.addEvent("no_waiting_ral", {
167
+ "delegation.etags_checked": eTags.length,
168
+ "delegation.first_etag": eTags[0],
169
+ });
170
+ span.setStatus({ code: SpanStatusCode.OK });
171
+ return { recorded: false };
172
+ }
173
+
174
+ span.setAttribute("delegation.event_id", delegationEventId || "unknown");
175
+
176
+ // Get the target agent for logging
177
+ const projectCtx = getProjectContext();
178
+
179
+ // Note: We don't spawn an execution here - AgentDispatchService handles that
180
+ // via delegationTarget detection. This handler just records the completion.
181
+
182
+ // Get counts from conversation storage
183
+ const pendingDelegations = ralRegistry.getConversationPendingDelegations(
184
+ location.agentPubkey, location.conversationId, location.ralNumber
185
+ );
186
+ const completedDelegations = ralRegistry.getConversationCompletedDelegations(
187
+ location.agentPubkey, location.conversationId, location.ralNumber
188
+ );
189
+
190
+ const targetAgent = projectCtx.getAgentByPubkey(location.agentPubkey);
191
+ const agentSlug = targetAgent?.slug;
192
+
193
+ span.setAttributes({
194
+ "agent.pubkey": location.agentPubkey,
195
+ "agent.slug": agentSlug || "unknown",
196
+ "conversation.id": shortenConversationId(location.conversationId),
197
+ "delegation.pending_count": pendingDelegations.length,
198
+ "delegation.completed_count": completedDelegations.length,
199
+ });
200
+
201
+ span.addEvent("completion_recorded", {
202
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
203
+ "delegation.event_id": delegationEventId!, // Non-null when location is set
204
+ "responder.pubkey": event.pubkey,
205
+ "response.length": event.content?.length || 0,
206
+ });
207
+ span.setStatus({ code: SpanStatusCode.OK });
208
+
209
+ logger.info("[handleDelegationCompletion] Recorded delegation completion", {
210
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
211
+ delegationEventId: delegationEventId!.substring(0, 8),
212
+ agentSlug,
213
+ conversationId: location.conversationId.substring(0, 8),
214
+ completionEventId: event.id?.substring(0, 8),
215
+ completedCount: completedDelegations.length,
216
+ pendingCount: pendingDelegations.length,
217
+ });
218
+
219
+ return {
220
+ recorded: true,
221
+ agentSlug,
222
+ conversationId: location.conversationId,
223
+ pendingCount: pendingDelegations.length,
224
+ };
225
+ } catch (error) {
226
+ span.recordException(error as Error);
227
+ span.setStatus({ code: SpanStatusCode.ERROR });
228
+ throw error;
229
+ } finally {
230
+ span.end();
231
+ }
232
+ }
@@ -0,0 +1,188 @@
1
+ import { type FeatureExtractionPipeline, type Tensor, pipeline } from "@huggingface/transformers";
2
+
3
+ export interface EmbeddingProvider {
4
+ /**
5
+ * Generate embedding for a single text
6
+ */
7
+ embed(text: string): Promise<Float32Array>;
8
+
9
+ /**
10
+ * Generate embeddings for multiple texts
11
+ */
12
+ embedBatch(texts: string[]): Promise<Float32Array[]>;
13
+
14
+ /**
15
+ * Get the dimension of the embeddings
16
+ */
17
+ getDimensions(): Promise<number>;
18
+
19
+ /**
20
+ * Get model identifier
21
+ */
22
+ getModelId(): string;
23
+ }
24
+
25
+ /**
26
+ * Local transformer-based embedding provider using @huggingface/transformers
27
+ */
28
+ export class LocalTransformerEmbeddingProvider implements EmbeddingProvider {
29
+ private extractorPipeline: FeatureExtractionPipeline | null = null;
30
+ private modelId: string;
31
+ private dimensions: number | null = null;
32
+ private initializationPromise: Promise<void> | null = null;
33
+
34
+ constructor(modelId = "Xenova/all-MiniLM-L6-v2") {
35
+ this.modelId = modelId;
36
+ this.initializationPromise = this.initialize();
37
+ }
38
+
39
+ /**
40
+ * Initialize the provider by generating a test embedding to determine dimensions.
41
+ * This ensures dimensions are always available after construction completes.
42
+ */
43
+ private async initialize(): Promise<void> {
44
+ try {
45
+ const pipe = await this.ensurePipeline();
46
+ const output = await pipe("test", { pooling: "mean", normalize: true });
47
+ const embedding = this.tensorToFloat32Array(output);
48
+ this.dimensions = embedding.length;
49
+ } catch (error) {
50
+ throw new Error(
51
+ `Failed to initialize embedding provider: ${
52
+ error instanceof Error ? error.message : String(error)
53
+ }`,
54
+ { cause: error }
55
+ );
56
+ }
57
+ }
58
+
59
+ private tensorToFloat32Array(tensor: Tensor): Float32Array {
60
+ return tensor.data instanceof Float32Array
61
+ ? tensor.data
62
+ : new Float32Array(tensor.data as ArrayLike<number>);
63
+ }
64
+
65
+ private async ensurePipeline(): Promise<FeatureExtractionPipeline> {
66
+ if (!this.extractorPipeline) {
67
+ this.extractorPipeline = await pipeline("feature-extraction", this.modelId);
68
+ }
69
+ return this.extractorPipeline;
70
+ }
71
+
72
+ public async embed(text: string): Promise<Float32Array> {
73
+ const embeddings = await this.embedBatch([text]);
74
+ return embeddings[0];
75
+ }
76
+
77
+ public async embedBatch(texts: string[]): Promise<Float32Array[]> {
78
+ await this.ensureInitialized();
79
+ const pipe = await this.ensurePipeline();
80
+ const results: Float32Array[] = [];
81
+
82
+ for (const text of texts) {
83
+ const output = await pipe(text, { pooling: "mean", normalize: true });
84
+ results.push(this.tensorToFloat32Array(output));
85
+ }
86
+
87
+ return results;
88
+ }
89
+
90
+ /**
91
+ * Ensure the provider has completed initialization.
92
+ * This guarantees dimensions are available before any operations.
93
+ */
94
+ private async ensureInitialized(): Promise<void> {
95
+ if (this.initializationPromise) {
96
+ await this.initializationPromise;
97
+ this.initializationPromise = null;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Get embedding dimensions.
103
+ * Dimensions are guaranteed to be available after initialization completes.
104
+ */
105
+ public async getDimensions(): Promise<number> {
106
+ await this.ensureInitialized();
107
+
108
+ if (this.dimensions === null) {
109
+ throw new Error(
110
+ "Embedding dimensions not available after initialization. " +
111
+ "This indicates a critical initialization failure."
112
+ );
113
+ }
114
+
115
+ return this.dimensions;
116
+ }
117
+
118
+ public getModelId(): string {
119
+ return this.modelId;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * OpenAI-compatible embedding provider
125
+ * Works with OpenAI, OpenRouter, and other OpenAI-compatible APIs
126
+ */
127
+ export class OpenAIEmbeddingProvider implements EmbeddingProvider {
128
+ private apiKey: string;
129
+ private modelId: string;
130
+ private baseUrl: string;
131
+ private dimensions: number | null = null;
132
+
133
+ constructor(apiKey: string, modelId = "text-embedding-3-small", baseUrl = "https://api.openai.com/v1") {
134
+ this.apiKey = apiKey;
135
+ this.modelId = modelId;
136
+ this.baseUrl = baseUrl.replace(/\/$/, ""); // Remove trailing slash
137
+ }
138
+
139
+ public async embed(text: string): Promise<Float32Array> {
140
+ const embeddings = await this.embedBatch([text]);
141
+ return embeddings[0];
142
+ }
143
+
144
+ public async embedBatch(texts: string[]): Promise<Float32Array[]> {
145
+ const response = await fetch(`${this.baseUrl}/embeddings`, {
146
+ method: "POST",
147
+ headers: {
148
+ "Content-Type": "application/json",
149
+ Authorization: `Bearer ${this.apiKey}`,
150
+ },
151
+ body: JSON.stringify({
152
+ model: this.modelId,
153
+ input: texts,
154
+ }),
155
+ });
156
+
157
+ if (!response.ok) {
158
+ throw new Error(`OpenAI API error: ${response.statusText}`);
159
+ }
160
+
161
+ interface EmbeddingResponse {
162
+ data: Array<{ embedding: number[] }>;
163
+ }
164
+
165
+ const data = (await response.json()) as EmbeddingResponse;
166
+
167
+ const embeddings = data.data.map((item) => new Float32Array(item.embedding));
168
+
169
+ // Cache dimensions from first successful response
170
+ if (this.dimensions === null && embeddings.length > 0) {
171
+ this.dimensions = embeddings[0].length;
172
+ }
173
+
174
+ return embeddings;
175
+ }
176
+
177
+ public async getDimensions(): Promise<number> {
178
+ // If we haven't cached dimensions yet, make a test embedding call
179
+ if (this.dimensions === null) {
180
+ await this.embed("test");
181
+ }
182
+ return this.dimensions!;
183
+ }
184
+
185
+ public getModelId(): string {
186
+ return this.modelId;
187
+ }
188
+ }
@@ -0,0 +1,5 @@
1
+ export type { EmbeddingProvider } from "./EmbeddingProvider";
2
+ export {
3
+ LocalTransformerEmbeddingProvider,
4
+ OpenAIEmbeddingProvider,
5
+ } from "./EmbeddingProvider";