@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,229 @@
1
+ import type { CompleteEvent, ContentEvent, StreamErrorEvent } from "@/llm/types";
2
+ import { config as configService } from "@/services/ConfigService";
3
+ import type { TenexLLMs } from "@/services/config/types";
4
+ import { isMetaModelConfiguration } from "@/services/config/types";
5
+ import chalk from "chalk";
6
+ import inquirer from "inquirer";
7
+ import { z } from "zod";
8
+ import { llmServiceFactory } from "../LLMServiceFactory";
9
+
10
+ /**
11
+ * Extended type for editor use - includes providers
12
+ */
13
+ type TenexLLMsWithProviders = TenexLLMs & {
14
+ providers: Record<string, { apiKey: string | string[] }>;
15
+ };
16
+
17
+ /**
18
+ * Tests LLM configurations
19
+ */
20
+ export class ConfigurationTester {
21
+ static async test(llmsConfig: TenexLLMsWithProviders): Promise<void> {
22
+ const configNames = Object.keys(llmsConfig.configurations);
23
+
24
+ if (configNames.length === 0) {
25
+ console.log(chalk.yellow("⚠️ No configurations to test"));
26
+ return;
27
+ }
28
+
29
+ const { name } = await inquirer.prompt([
30
+ {
31
+ type: "select",
32
+ name: "name",
33
+ message: "Select configuration to test:",
34
+ choices: configNames.map((n) => {
35
+ const cfg = llmsConfig.configurations[n];
36
+ const isMeta = isMetaModelConfiguration(cfg);
37
+ const label = n === llmsConfig.default ? `${n} (default)` : n;
38
+ return {
39
+ name: isMeta ? `${label} [meta model]` : label,
40
+ value: n,
41
+ };
42
+ }),
43
+ },
44
+ ]);
45
+
46
+ try {
47
+ // Load full config first (needed for getLLMConfig and MCP server configs)
48
+ await configService.loadConfig();
49
+
50
+ // Use getLLMConfig to resolve meta models to their default variant
51
+ const llmConfig = configService.getLLMConfig(name);
52
+ const rawConfig = llmsConfig.configurations[name];
53
+ const isMeta = isMetaModelConfiguration(rawConfig);
54
+
55
+ console.log(chalk.yellow(`\nTesting configuration "${name}"${isMeta ? " (meta model - using default variant)" : ""}...`));
56
+ console.log(chalk.gray(`Provider: ${llmConfig.provider}, Model: ${llmConfig.model}`));
57
+
58
+ // Initialize providers before testing
59
+ await llmServiceFactory.initializeProviders(llmsConfig.providers);
60
+
61
+ // Create the service using the factory
62
+ const service = llmServiceFactory.createService(llmConfig);
63
+
64
+ console.log(chalk.cyan("📡 Sending test message..."));
65
+ const handleContent = (event: ContentEvent): void => {
66
+ process.stdout.write(chalk.cyan(event.delta));
67
+ };
68
+ service.on("content", handleContent);
69
+
70
+ const completePromise = new Promise<CompleteEvent>((resolve) => {
71
+ service.once("complete", resolve);
72
+ });
73
+ const errorPromise = new Promise<never>((_resolve, reject) => {
74
+ service.once("stream-error", (event: StreamErrorEvent) => {
75
+ reject(event.error);
76
+ });
77
+ });
78
+
79
+ const completion = Promise.race([completePromise, errorPromise]);
80
+
81
+ console.log(chalk.white("Response: "));
82
+ const [, completeEvent] = await Promise.all([
83
+ service.stream(
84
+ [{ role: "user", content: "Say 'Hello, TENEX!' in exactly those words." }],
85
+ {}
86
+ ),
87
+ completion,
88
+ ]);
89
+
90
+ process.stdout.write("\n");
91
+ console.log(chalk.green("✅ Test successful!"));
92
+
93
+ // Show usage stats if available
94
+ if (completeEvent.usage) {
95
+ const usage = completeEvent.usage;
96
+ const inputTokens = usage.inputTokens ?? "?";
97
+ const outputTokens = usage.outputTokens ?? "?";
98
+ const totalTokens = usage.totalTokens ?? "?";
99
+ console.log(
100
+ chalk.gray(`\nTokens: ${inputTokens} + ${outputTokens} = ${totalTokens}`)
101
+ );
102
+ }
103
+
104
+ await new Promise((resolve) => setTimeout(resolve, 2000));
105
+ } catch (error: unknown) {
106
+ console.log(chalk.red("\n❌ Test failed!"));
107
+
108
+ const errorMessage = error instanceof Error ? error.message : String(error);
109
+ if (errorMessage) {
110
+ console.log(chalk.red(`Error: ${errorMessage}`));
111
+ }
112
+
113
+ // Check for common issues
114
+ if (errorMessage?.includes("401") || errorMessage?.includes("Unauthorized")) {
115
+ console.log(chalk.yellow("\n💡 Invalid or expired API key"));
116
+ } else if (errorMessage?.includes("404")) {
117
+ console.log(chalk.yellow(`\n💡 Model for configuration '${name}' may not be available`));
118
+ } else if (errorMessage?.includes("rate limit")) {
119
+ console.log(chalk.yellow("\n💡 Rate limit hit. Please wait and try again"));
120
+ }
121
+
122
+ await new Promise((resolve) => setTimeout(resolve, 3000));
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Test a configuration for summarization using generateObject
128
+ */
129
+ static async testSummarization(llmsConfig: TenexLLMsWithProviders, configName: string): Promise<void> {
130
+ const rawConfig = llmsConfig.configurations[configName];
131
+ if (!rawConfig) {
132
+ console.log(chalk.red(`❌ Configuration "${configName}" not found`));
133
+ return;
134
+ }
135
+
136
+ // Use getLLMConfig to resolve meta models to their default variant
137
+ const llmConfig = configService.getLLMConfig(configName);
138
+ const isMeta = isMetaModelConfiguration(rawConfig);
139
+
140
+ console.log(chalk.yellow(`\nTesting summarization with "${configName}"${isMeta ? " (meta model - using default variant)" : ""}...`));
141
+ console.log(chalk.gray(`Provider: ${llmConfig.provider}, Model: ${llmConfig.model}`));
142
+
143
+ // Schema that mimics what we'd use for kind 513 summaries
144
+ const SummarySchema = z.object({
145
+ title: z.string().describe("A brief title for the summary"),
146
+ summary: z.string().describe("A concise summary of the conversation"),
147
+ keyPoints: z.array(z.string()).describe("Key points from the conversation"),
148
+ });
149
+
150
+ try {
151
+ // Load full config (needed for MCP server configs in agent providers)
152
+ await configService.loadConfig();
153
+
154
+ // Initialize providers before testing
155
+ await llmServiceFactory.initializeProviders(llmsConfig.providers);
156
+
157
+ // Create the service using the factory
158
+ const service = llmServiceFactory.createService(llmConfig);
159
+
160
+ console.log(chalk.cyan("📡 Testing generateObject..."));
161
+
162
+ const testConversation = `
163
+ User: I need help setting up authentication for my web app.
164
+ Assistant: I can help with that. What authentication method are you considering?
165
+ User: I'm thinking OAuth with Google and GitHub.
166
+ Assistant: Great choice. OAuth is secure and user-friendly. Let me outline the steps...
167
+ `;
168
+
169
+ const result = await service.generateObject(
170
+ [
171
+ {
172
+ role: "system",
173
+ content:
174
+ "You are a helpful assistant that summarizes conversations. Generate a structured summary.",
175
+ },
176
+ {
177
+ role: "user",
178
+ content: `Summarize this conversation:\n${testConversation}`,
179
+ },
180
+ ],
181
+ SummarySchema
182
+ );
183
+
184
+ console.log(chalk.green("\n✅ generateObject test successful!"));
185
+ console.log(chalk.white("\nGenerated summary:"));
186
+ console.log(chalk.cyan(` Title: ${result.object.title}`));
187
+ console.log(chalk.cyan(` Summary: ${result.object.summary}`));
188
+ console.log(chalk.cyan(" Key Points:"));
189
+ for (const point of result.object.keyPoints) {
190
+ console.log(chalk.cyan(` • ${point}`));
191
+ }
192
+
193
+ // Show usage stats if available
194
+ if (result.usage) {
195
+ const { inputTokens, outputTokens, totalTokens } = result.usage;
196
+ console.log(chalk.gray(`\nTokens: ${inputTokens} + ${outputTokens} = ${totalTokens}`));
197
+ }
198
+
199
+ await new Promise((resolve) => setTimeout(resolve, 2000));
200
+ } catch (error: unknown) {
201
+ console.log(chalk.red("\n❌ generateObject test failed!"));
202
+
203
+ const errorMessage = error instanceof Error ? error.message : String(error);
204
+ if (errorMessage) {
205
+ console.log(chalk.red(`Error: ${errorMessage}`));
206
+ }
207
+
208
+ // Check for common issues
209
+ if (errorMessage?.includes("401") || errorMessage?.includes("Unauthorized")) {
210
+ console.log(chalk.yellow("\n💡 Invalid or expired API key"));
211
+ } else if (errorMessage?.includes("404")) {
212
+ console.log(chalk.yellow(`\n💡 Model '${llmConfig.model}' may not be available`));
213
+ } else if (errorMessage?.includes("rate limit")) {
214
+ console.log(chalk.yellow("\n💡 Rate limit hit. Please wait and try again"));
215
+ } else if (
216
+ errorMessage?.includes("structured output") ||
217
+ errorMessage?.includes("json")
218
+ ) {
219
+ console.log(
220
+ chalk.yellow(
221
+ "\n💡 This model may not support structured output (generateObject)"
222
+ )
223
+ );
224
+ }
225
+
226
+ await new Promise((resolve) => setTimeout(resolve, 3000));
227
+ }
228
+ }
229
+ }
@@ -0,0 +1,212 @@
1
+ import chalk from "chalk";
2
+ import inquirer from "inquirer";
3
+ import { fetchOllamaModels, getPopularOllamaModels } from "../providers/ollama-models";
4
+ import { fetchOpenRouterModels, getPopularModels } from "../providers/openrouter-models";
5
+
6
+ /**
7
+ * Utility class for interactive model selection
8
+ * Extracted from LLMConfigEditor to reduce complexity
9
+ */
10
+ export class ModelSelector {
11
+ /**
12
+ * Select an Ollama model interactively
13
+ */
14
+ static async selectOllamaModel(currentModel?: string): Promise<string> {
15
+ console.log(chalk.gray("Fetching available Ollama models..."));
16
+
17
+ const ollamaModels = await fetchOllamaModels();
18
+
19
+ if (ollamaModels.length > 0) {
20
+ console.log(chalk.green(`✓ Found ${ollamaModels.length} installed models`));
21
+
22
+ const choices = [
23
+ ...ollamaModels.map((m) => ({
24
+ name: `${m.name} ${chalk.gray(`(${m.size})`)}`,
25
+ value: m.name,
26
+ })),
27
+ new inquirer.Separator(),
28
+ { name: chalk.cyan("→ Type model name manually"), value: "__manual__" },
29
+ ];
30
+
31
+ const { selectedModel } = await inquirer.prompt([
32
+ {
33
+ type: "select",
34
+ name: "selectedModel",
35
+ message: "Select model:",
36
+ choices,
37
+ default: currentModel,
38
+ pageSize: 15,
39
+ },
40
+ ]);
41
+
42
+ if (selectedModel === "__manual__") {
43
+ return await ModelSelector.promptManualModel(currentModel || "llama3.1:8b");
44
+ }
45
+
46
+ return selectedModel;
47
+ }
48
+ console.log(chalk.yellow("⚠️ No Ollama models found. Make sure Ollama is running."));
49
+ console.log(chalk.gray("Showing popular models (you'll need to pull them first)."));
50
+
51
+ const popular = getPopularOllamaModels();
52
+ const choices = [];
53
+ for (const [category, models] of Object.entries(popular)) {
54
+ choices.push(new inquirer.Separator(`--- ${category} ---`));
55
+ choices.push(
56
+ ...models.map((m) => ({
57
+ name: m,
58
+ value: m,
59
+ }))
60
+ );
61
+ }
62
+
63
+ choices.push(new inquirer.Separator());
64
+ choices.push({ name: chalk.cyan("→ Type model name manually"), value: "__manual__" });
65
+
66
+ const { selectedModel } = await inquirer.prompt([
67
+ {
68
+ type: "select",
69
+ name: "selectedModel",
70
+ message: "Select model:",
71
+ default: currentModel,
72
+ choices,
73
+ pageSize: 15,
74
+ },
75
+ ]);
76
+
77
+ if (selectedModel === "__manual__") {
78
+ return await ModelSelector.promptManualModel(currentModel || "llama3.1:8b");
79
+ }
80
+
81
+ return selectedModel;
82
+ }
83
+
84
+ /**
85
+ * Select an OpenRouter model interactively
86
+ */
87
+ static async selectOpenRouterModel(currentModel?: string): Promise<string> {
88
+ console.log(chalk.gray("Fetching available OpenRouter models..."));
89
+
90
+ const openRouterModels = await fetchOpenRouterModels();
91
+
92
+ if (openRouterModels.length > 0) {
93
+ console.log(chalk.green(`✓ Found ${openRouterModels.length} available models`));
94
+
95
+ // Group models by provider
96
+ const modelsByProvider: Record<string, typeof openRouterModels> = {};
97
+ for (const model of openRouterModels) {
98
+ const provider = model.id.split("/")[0] || "other";
99
+ if (!modelsByProvider[provider]) {
100
+ modelsByProvider[provider] = [];
101
+ }
102
+ modelsByProvider[provider].push(model);
103
+ }
104
+
105
+ // Build choices
106
+ const choices = [];
107
+ const sortedProviders = Object.keys(modelsByProvider).sort();
108
+
109
+ for (const provider of sortedProviders) {
110
+ choices.push(
111
+ new inquirer.Separator(chalk.yellow(`--- ${provider.toUpperCase()} ---`))
112
+ );
113
+ const providerModels = modelsByProvider[provider];
114
+
115
+ for (const model of providerModels) {
116
+ const pricing = `$${model.pricing.prompt}/$${model.pricing.completion}/1M`;
117
+ const context = `${Math.round(model.context_length / 1000)}k`;
118
+ const freeTag = model.id.endsWith(":free") ? chalk.green(" [FREE]") : "";
119
+
120
+ choices.push({
121
+ name: `${model.id}${freeTag} ${chalk.gray(`- ${context} context, ${pricing}`)}`,
122
+ value: model.id,
123
+ short: model.id,
124
+ });
125
+ }
126
+ }
127
+
128
+ choices.push(new inquirer.Separator());
129
+ choices.push({ name: chalk.cyan("→ Type model ID manually"), value: "__manual__" });
130
+
131
+ const { selectedModel } = await inquirer.prompt([
132
+ {
133
+ type: "select",
134
+ name: "selectedModel",
135
+ message: "Select model:",
136
+ choices,
137
+ default: currentModel,
138
+ pageSize: 20,
139
+ loop: false,
140
+ },
141
+ ]);
142
+
143
+ if (selectedModel === "__manual__") {
144
+ return await ModelSelector.promptManualModel(currentModel || "openai/gpt-4");
145
+ }
146
+
147
+ return selectedModel;
148
+ }
149
+ console.log(chalk.yellow("⚠️ Failed to fetch models from OpenRouter API"));
150
+ console.log(
151
+ chalk.gray("You can still enter a model ID manually or select from popular models.")
152
+ );
153
+
154
+ const { selectionMethod } = await inquirer.prompt([
155
+ {
156
+ type: "select",
157
+ name: "selectionMethod",
158
+ message: "How would you like to select the model?",
159
+ choices: [
160
+ { name: "Quick select from popular models", value: "quick" },
161
+ { name: "Type model ID manually", value: "manual" },
162
+ ],
163
+ },
164
+ ]);
165
+
166
+ if (selectionMethod === "quick") {
167
+ const popular = getPopularModels();
168
+ const choices = [];
169
+ for (const [category, models] of Object.entries(popular)) {
170
+ choices.push(new inquirer.Separator(`--- ${category} ---`));
171
+ choices.push(
172
+ ...models.map((m) => ({
173
+ name: m,
174
+ value: m,
175
+ }))
176
+ );
177
+ }
178
+
179
+ const { selectedModel } = await inquirer.prompt([
180
+ {
181
+ type: "select",
182
+ name: "selectedModel",
183
+ message: "Select model:",
184
+ default: currentModel,
185
+ choices,
186
+ pageSize: 15,
187
+ },
188
+ ]);
189
+ return selectedModel;
190
+ }
191
+ return await ModelSelector.promptManualModel(currentModel || "openai/gpt-4");
192
+ }
193
+
194
+ /**
195
+ * Prompt for manual model entry
196
+ */
197
+ private static async promptManualModel(defaultValue: string): Promise<string> {
198
+ const { inputModel } = await inquirer.prompt([
199
+ {
200
+ type: "input",
201
+ name: "inputModel",
202
+ message: "Enter model name/ID:",
203
+ default: defaultValue,
204
+ validate: (input: string) => {
205
+ if (!input.trim()) return "Model name is required";
206
+ return true;
207
+ },
208
+ },
209
+ ]);
210
+ return inputModel;
211
+ }
212
+ }
@@ -0,0 +1,177 @@
1
+ import { PROVIDER_IDS } from "@/llm/providers/provider-ids";
2
+ import { resolveApiKey, hasApiKey } from "@/llm/providers/key-manager";
3
+ import type { TenexLLMs } from "@/services/config/types";
4
+ import chalk from "chalk";
5
+ import inquirer from "inquirer";
6
+
7
+ /**
8
+ * Extended type for editor use - includes providers
9
+ */
10
+ type TenexLLMsWithProviders = TenexLLMs & {
11
+ providers: Record<string, { apiKey: string | string[] }>;
12
+ };
13
+
14
+ /**
15
+ * UI utilities for provider configuration
16
+ * Extracted from LLMConfigEditor to separate concerns
17
+ */
18
+ export class ProviderConfigUI {
19
+ /**
20
+ * Get provider display names
21
+ */
22
+ static getProviderDisplayName(provider: string): string {
23
+ const names: Record<string, string> = {
24
+ [PROVIDER_IDS.OPENROUTER]: "OpenRouter (300+ models)",
25
+ [PROVIDER_IDS.ANTHROPIC]: "Anthropic (Claude)",
26
+ [PROVIDER_IDS.OPENAI]: "OpenAI (GPT)",
27
+ [PROVIDER_IDS.OLLAMA]: "Ollama (Local models)",
28
+ [PROVIDER_IDS.CLAUDE_CODE]: "Claude Code",
29
+ [PROVIDER_IDS.CODEX_APP_SERVER]: "Codex App Server (GPT-5.1/5.2)",
30
+ };
31
+ return names[provider] || provider;
32
+ }
33
+
34
+ /**
35
+ * Configure a specific provider interactively
36
+ */
37
+ static async configureProvider(
38
+ provider: string,
39
+ currentProviders?: Record<string, { apiKey: string | string[] }>
40
+ ): Promise<{ apiKey: string | string[] }> {
41
+ if (provider === PROVIDER_IDS.CLAUDE_CODE || provider === PROVIDER_IDS.CODEX_APP_SERVER) {
42
+ // Agent providers don't require an API key
43
+ console.log(
44
+ chalk.green(
45
+ `✓ ${ProviderConfigUI.getProviderDisplayName(provider)} provider configured (no API key required)`
46
+ )
47
+ );
48
+ return { apiKey: "none" }; // Doesn't use API keys
49
+ }
50
+ if (provider === PROVIDER_IDS.OLLAMA) {
51
+ // For Ollama, ask for base URL instead of API key
52
+ const currentUrl = resolveApiKey(currentProviders?.[provider]?.apiKey) || "local";
53
+ const { ollamaConfig } = await inquirer.prompt([
54
+ {
55
+ type: "select",
56
+ name: "ollamaConfig",
57
+ message: "Ollama configuration:",
58
+ choices: [
59
+ { name: "Use local Ollama (http://localhost:11434)", value: "local" },
60
+ { name: "Use custom Ollama URL", value: "custom" },
61
+ ],
62
+ default: currentUrl === "local" ? "local" : "custom",
63
+ },
64
+ ]);
65
+
66
+ let baseUrl = "local";
67
+ if (ollamaConfig === "custom") {
68
+ const { customUrl } = await inquirer.prompt([
69
+ {
70
+ type: "input",
71
+ name: "customUrl",
72
+ message: "Enter Ollama base URL:",
73
+ default: currentUrl !== "local" ? currentUrl : "http://localhost:11434",
74
+ validate: (input: string) => {
75
+ if (!input.trim()) return "URL is required";
76
+ try {
77
+ new URL(input);
78
+ return true;
79
+ } catch {
80
+ return "Please enter a valid URL";
81
+ }
82
+ },
83
+ },
84
+ ]);
85
+ baseUrl = customUrl;
86
+ }
87
+
88
+ return { apiKey: baseUrl };
89
+ }
90
+ // For other providers, ask for API key
91
+ const existingKey = currentProviders?.[provider]?.apiKey;
92
+
93
+ // If the provider already has a multi-key array, preserve it
94
+ if (Array.isArray(existingKey) && existingKey.length > 1) {
95
+ console.log(
96
+ chalk.cyan(
97
+ ` ℹ ${ProviderConfigUI.getProviderDisplayName(provider)} has ${existingKey.length} API keys configured (multi-key rotation).`
98
+ )
99
+ );
100
+ console.log(
101
+ chalk.gray(
102
+ " To edit multi-key configs, modify providers.json directly."
103
+ )
104
+ );
105
+ const { keep } = await inquirer.prompt([
106
+ {
107
+ type: "confirm",
108
+ name: "keep",
109
+ message: "Keep existing multi-key configuration?",
110
+ default: true,
111
+ },
112
+ ]);
113
+ if (keep) {
114
+ return { apiKey: existingKey };
115
+ }
116
+ }
117
+
118
+ const currentKey = resolveApiKey(existingKey);
119
+ const { apiKey } = await inquirer.prompt([
120
+ {
121
+ type: "password",
122
+ name: "apiKey",
123
+ message: `Enter API key for ${ProviderConfigUI.getProviderDisplayName(provider)} (press Enter to keep existing):`,
124
+ default: currentKey,
125
+ mask: "*",
126
+ validate: (input: string) => {
127
+ // Allow empty input if there's an existing key
128
+ if (!input.trim() && !currentKey) return "API key is required";
129
+ return true;
130
+ },
131
+ },
132
+ ]);
133
+
134
+ return { apiKey: apiKey || currentKey || "" };
135
+ }
136
+
137
+ /**
138
+ * Display current configuration status
139
+ */
140
+ static displayCurrentConfig(llmsConfig: TenexLLMsWithProviders): void {
141
+ console.log(chalk.bold("Configured Providers:"));
142
+ const providers = Object.keys(llmsConfig.providers).filter(
143
+ (p) => hasApiKey(llmsConfig.providers[p]?.apiKey)
144
+ );
145
+ if (providers.length === 0) {
146
+ console.log(chalk.gray(" None configured"));
147
+ } else {
148
+ for (const p of providers) {
149
+ console.log(chalk.green(` ✓ ${ProviderConfigUI.getProviderDisplayName(p)}`));
150
+ }
151
+ }
152
+
153
+ console.log(chalk.bold("\nConfigurations:"));
154
+ const configNames = Object.keys(llmsConfig.configurations);
155
+ if (configNames.length === 0) {
156
+ console.log(chalk.gray(" None"));
157
+ } else {
158
+ for (const name of configNames) {
159
+ const config = llmsConfig.configurations[name];
160
+ const isDefault = name === llmsConfig.default;
161
+ const isSummarization = name === llmsConfig.summarization;
162
+ const marker = isDefault || isSummarization ? chalk.cyan("• ") : " ";
163
+
164
+ const tags: string[] = [];
165
+ if (isDefault) tags.push("default");
166
+ if (isSummarization) tags.push("summarization");
167
+ const tagStr = tags.length > 0 ? chalk.gray(` (${tags.join(", ")})`) : "";
168
+
169
+ // Handle meta models differently - they don't have a single model
170
+ const configDisplay = config.provider === "meta"
171
+ ? `meta (${Object.keys((config as { variants: Record<string, unknown> }).variants).length} variants)`
172
+ : `${config.provider}:${"model" in config ? config.model : "unknown"}`;
173
+ console.log(` ${marker}${name}${tagStr}: ${configDisplay}`);
174
+ }
175
+ }
176
+ }
177
+ }