@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,90 @@
1
+ import type { ModelMessage } from "ai";
2
+ import { PROVIDER_IDS } from "./providers/provider-ids";
3
+
4
+ /**
5
+ * Add provider-specific cache control to messages.
6
+ * Only Anthropic requires explicit cache control; OpenAI and Gemini cache automatically.
7
+ */
8
+ export function addCacheControl(messages: ModelMessage[], provider: string): ModelMessage[] {
9
+ // Only add cache control for Anthropic
10
+ if (provider !== PROVIDER_IDS.ANTHROPIC) {
11
+ return messages;
12
+ }
13
+
14
+ // Rough estimate: 4 characters per token (configurable if needed)
15
+ const CHARS_PER_TOKEN_ESTIMATE = 4;
16
+ const MIN_TOKENS_FOR_CACHE = 1024;
17
+ const minCharsForCache = MIN_TOKENS_FOR_CACHE * CHARS_PER_TOKEN_ESTIMATE;
18
+
19
+ return messages.map((msg) => {
20
+ // Only cache system messages and only if they're large enough
21
+ if (msg.role === "system" && typeof msg.content === "string" && msg.content.length > minCharsForCache) {
22
+ return {
23
+ ...msg,
24
+ providerOptions: {
25
+ anthropic: {
26
+ cacheControl: { type: "ephemeral" },
27
+ },
28
+ },
29
+ };
30
+ }
31
+ return msg;
32
+ });
33
+ }
34
+
35
+ /**
36
+ * Prepare messages for sending to the LLM.
37
+ * Handles provider-specific transformations.
38
+ */
39
+ export function prepareMessagesForRequest(messages: ModelMessage[], provider: string): ModelMessage[] {
40
+ let processedMessages = messages;
41
+
42
+ // For Claude Code, filter out system messages since they're passed via systemPrompt
43
+ if (provider === PROVIDER_IDS.CLAUDE_CODE) {
44
+ processedMessages = messages.filter((m) => m.role !== "system");
45
+ }
46
+
47
+ return addCacheControl(processedMessages, provider);
48
+ }
49
+
50
+ /**
51
+ * Extract the last user message text from a message array.
52
+ * Handles both simple string content and complex content arrays.
53
+ */
54
+ export function extractLastUserMessage(messages: ModelMessage[]): string | undefined {
55
+ // Find the last message with role "user" (iterate from end)
56
+ for (let i = messages.length - 1; i >= 0; i--) {
57
+ const msg = messages[i];
58
+ if (msg.role === "user") {
59
+ // User messages can have string content or content array
60
+ if (typeof msg.content === "string") {
61
+ return msg.content;
62
+ }
63
+ if (Array.isArray(msg.content)) {
64
+ // Extract text from content parts
65
+ const textParts = msg.content
66
+ .filter((part): part is { type: "text"; text: string } =>
67
+ part.type === "text" && typeof part.text === "string"
68
+ )
69
+ .map((part) => part.text);
70
+ if (textParts.length > 0) {
71
+ return textParts.join("\n");
72
+ }
73
+ }
74
+ // Found user message but couldn't extract text
75
+ return "[User message with non-text content]";
76
+ }
77
+ }
78
+ return undefined;
79
+ }
80
+
81
+ /**
82
+ * Extract system messages from a message array and combine them.
83
+ * Used for providers like Claude Code that take system prompt separately.
84
+ */
85
+ export function extractSystemContent(messages: ModelMessage[]): string {
86
+ return messages
87
+ .filter((m) => m.role === "system")
88
+ .map((m) => (typeof m.content === "string" ? m.content : JSON.stringify(m.content)))
89
+ .join("\n\n");
90
+ }
@@ -0,0 +1,37 @@
1
+ import { EventEmitter } from "tseep";
2
+
3
+ /**
4
+ * Global recording state singleton.
5
+ * Controls whether the flight recorder middleware saves LLM interactions.
6
+ */
7
+ class RecordingStateManager extends EventEmitter<{
8
+ "state-changed": (isRecording: boolean) => void;
9
+ }> {
10
+ private _isRecording = false;
11
+
12
+ get isRecording(): boolean {
13
+ return this._isRecording;
14
+ }
15
+
16
+ toggle(): boolean {
17
+ this._isRecording = !this._isRecording;
18
+ this.emit("state-changed", this._isRecording);
19
+ return this._isRecording;
20
+ }
21
+
22
+ start(): void {
23
+ if (!this._isRecording) {
24
+ this._isRecording = true;
25
+ this.emit("state-changed", true);
26
+ }
27
+ }
28
+
29
+ stop(): void {
30
+ if (this._isRecording) {
31
+ this._isRecording = false;
32
+ this.emit("state-changed", false);
33
+ }
34
+ }
35
+ }
36
+
37
+ export const recordingState = new RecordingStateManager();
@@ -0,0 +1,40 @@
1
+ import { logger } from "@/utils/logger";
2
+ import type { LocalStreamChunk } from "./types";
3
+
4
+ /**
5
+ * Interface for stream transports (Unix socket, future Nostr ephemeral)
6
+ */
7
+ export interface StreamTransport {
8
+ write(chunk: LocalStreamChunk): void;
9
+ isConnected(): boolean;
10
+ }
11
+
12
+ /**
13
+ * Publishes AI SDK chunks to connected transports
14
+ * Fire-and-forget: silently drops if no transport connected
15
+ */
16
+ export class StreamPublisher {
17
+ private transport: StreamTransport | null = null;
18
+
19
+ setTransport(transport: StreamTransport | null): void {
20
+ this.transport = transport;
21
+ }
22
+
23
+ write(chunk: LocalStreamChunk): void {
24
+ logger.debug("[StreamPublisher] write called", {
25
+ hasTransport: !!this.transport,
26
+ isConnected: this.transport?.isConnected() ?? false,
27
+ chunkType: (chunk.data as { type?: string })?.type,
28
+ });
29
+ if (this.transport?.isConnected()) {
30
+ this.transport.write(chunk);
31
+ }
32
+ }
33
+
34
+ isConnected(): boolean {
35
+ return this.transport?.isConnected() ?? false;
36
+ }
37
+ }
38
+
39
+ /** Singleton instance */
40
+ export const streamPublisher = new StreamPublisher();
@@ -0,0 +1,77 @@
1
+ import { trace } from "@opentelemetry/api";
2
+ import type { TelemetrySettings } from "ai";
3
+ import { shortenConversationId } from "@/utils/conversation-id";
4
+
5
+ /**
6
+ * Get trace correlation ID for OpenRouter.
7
+ * Returns a string combining trace and span IDs for unique request identification.
8
+ */
9
+ export function getTraceCorrelationId(): string | undefined {
10
+ const span = trace.getActiveSpan();
11
+ if (!span) return undefined;
12
+ const ctx = span.spanContext();
13
+ return `tenex-${ctx.traceId}-${ctx.spanId}`;
14
+ }
15
+
16
+ /**
17
+ * @deprecated Use shortenConversationId from @/utils/conversation-id instead.
18
+ * This function is kept for backward compatibility but will be removed in a future version.
19
+ */
20
+ export function shortenConversationIdForSpan(conversationId: string): string {
21
+ return shortenConversationId(conversationId);
22
+ }
23
+
24
+ /**
25
+ * Get OpenRouter metadata for request correlation.
26
+ * Includes OTL trace context plus agent and conversation identifiers.
27
+ */
28
+ export function getOpenRouterMetadata(
29
+ agentSlug?: string,
30
+ conversationId?: string
31
+ ): Record<string, string> {
32
+ const metadata: Record<string, string> = {};
33
+
34
+ const span = trace.getActiveSpan();
35
+ if (span) {
36
+ const ctx = span.spanContext();
37
+ metadata.tenex_trace_id = ctx.traceId;
38
+ metadata.tenex_span_id = ctx.spanId;
39
+ }
40
+
41
+ if (agentSlug) metadata.tenex_agent = agentSlug;
42
+ if (conversationId) metadata.tenex_conversation = conversationId;
43
+
44
+ return metadata;
45
+ }
46
+
47
+ /**
48
+ * Get full telemetry configuration for AI SDK.
49
+ * Captures EVERYTHING for debugging - no privacy filters.
50
+ */
51
+ export function getFullTelemetryConfig(config: {
52
+ agentSlug?: string;
53
+ provider: string;
54
+ model: string;
55
+ temperature?: number;
56
+ maxTokens?: number;
57
+ sessionId?: string;
58
+ }): TelemetrySettings {
59
+ return {
60
+ isEnabled: true,
61
+ functionId: `${config.agentSlug || "unknown"}.${config.provider}.${config.model}`,
62
+
63
+ // Metadata for debugging context
64
+ metadata: {
65
+ "agent.slug": config.agentSlug || "unknown",
66
+ "llm.provider": config.provider,
67
+ "llm.model": config.model,
68
+ "llm.temperature": config.temperature ?? 0,
69
+ "llm.max_tokens": config.maxTokens ?? 0,
70
+ "session.id": config.sessionId ?? "unknown",
71
+ },
72
+
73
+ // FULL DATA - no privacy filters for debugging
74
+ recordInputs: true, // Capture full prompts
75
+ recordOutputs: true, // Capture full responses
76
+ };
77
+ }
@@ -0,0 +1,57 @@
1
+ import { trace } from "@opentelemetry/api";
2
+ import type { TextStreamPart } from "ai";
3
+ import type { AISdkTool } from "@/tools/types";
4
+
5
+ type StreamChunk = TextStreamPart<Record<string, AISdkTool>>;
6
+
7
+ interface ChunkValidator {
8
+ name: string;
9
+ shouldIgnore: (chunk: StreamChunk) => boolean;
10
+ }
11
+
12
+ /**
13
+ * Validates reasoning-delta chunks with [REDACTED] content.
14
+ * Some LLM providers (e.g., OpenRouter with Gemini) send encrypted reasoning
15
+ * data that appears as "[REDACTED]" - these should be ignored.
16
+ */
17
+ const redactedReasoningValidator: ChunkValidator = {
18
+ name: "redacted-reasoning",
19
+ shouldIgnore: (chunk: StreamChunk): boolean => {
20
+ if (chunk.type !== "reasoning-delta") {
21
+ return false;
22
+ }
23
+
24
+ // The chunk may have 'text' or 'delta' property depending on how AI SDK processes it
25
+ const content = (chunk as { text?: string; delta?: string }).text
26
+ ?? (chunk as { text?: string; delta?: string }).delta;
27
+
28
+ return content === "[REDACTED]";
29
+ },
30
+ };
31
+
32
+ /**
33
+ * All chunk validators to apply before processing
34
+ */
35
+ const validators: ChunkValidator[] = [
36
+ redactedReasoningValidator,
37
+ ];
38
+
39
+ /**
40
+ * Check if a chunk should be ignored based on all validators.
41
+ * Adds an OTL span event when a chunk is ignored for debugging.
42
+ *
43
+ * @returns true if the chunk should be ignored, false otherwise
44
+ */
45
+ export function shouldIgnoreChunk(chunk: StreamChunk): boolean {
46
+ for (const validator of validators) {
47
+ if (validator.shouldIgnore(chunk)) {
48
+ const activeSpan = trace.getActiveSpan();
49
+ activeSpan?.addEvent("chunk_ignored", {
50
+ "chunk.type": chunk.type,
51
+ "validator.name": validator.name,
52
+ });
53
+ return true;
54
+ }
55
+ }
56
+ return false;
57
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * LLM Configuration Constants
3
+ */
4
+
5
+ // Default configuration name for agents when no llmConfig is specified
6
+ export const DEFAULT_AGENT_LLM_CONFIG = "default";
@@ -0,0 +1,12 @@
1
+ // Export service
2
+ export { LLMService } from "./service";
3
+
4
+ // Export factory
5
+ export { LLMServiceFactory, llmServiceFactory } from "./LLMServiceFactory";
6
+
7
+ // Export stream publisher
8
+ export { StreamPublisher, streamPublisher, type StreamTransport } from "./StreamPublisher";
9
+ export type { LocalStreamChunk } from "./types";
10
+
11
+ // Export types
12
+ export * from "./types";
@@ -0,0 +1,352 @@
1
+ /**
2
+ * MetaModelResolver - Handles dynamic model selection based on keywords
3
+ *
4
+ * Meta models are virtual model configurations that select from underlying
5
+ * real models based on keywords in the user's message. This enables
6
+ * "think harder" type functionality without explicit tool calls.
7
+ *
8
+ * @example
9
+ * User: "ultrathink how do we implement authentication?"
10
+ * → Resolves to high-cost reasoning model, strips "ultrathink" from message
11
+ *
12
+ * User: "quick what's 2+2?"
13
+ * → Resolves to fast model, strips "quick" from message
14
+ */
15
+
16
+ import type { MetaModelConfiguration, MetaModelVariant } from "@/services/config/types";
17
+ import { logger } from "@/utils/logger";
18
+
19
+ /**
20
+ * Result of resolving a meta model variant
21
+ */
22
+ export interface MetaModelResolution {
23
+ /** The resolved variant name */
24
+ variantName: string;
25
+ /** The variant configuration */
26
+ variant: MetaModelVariant;
27
+ /** The underlying LLM configuration name to use */
28
+ configName: string;
29
+ /** The message with keywords stripped (if stripKeywords was true) */
30
+ strippedMessage?: string;
31
+ /** Keywords that were matched */
32
+ matchedKeywords: string[];
33
+ /** Optional additional system prompt from the variant */
34
+ systemPrompt?: string;
35
+ }
36
+
37
+ /**
38
+ * Options for resolving a meta model
39
+ */
40
+ export interface ResolveOptions {
41
+ /** Whether to strip matched keywords from the message (default: true) */
42
+ stripKeywords?: boolean;
43
+ }
44
+
45
+ /**
46
+ * MetaModelResolver handles keyword detection and model resolution
47
+ * for meta model configurations.
48
+ */
49
+ export class MetaModelResolver {
50
+ /**
51
+ * Build a keyword-to-variant lookup map from a meta model configuration.
52
+ * Keywords are normalized to lowercase for case-insensitive matching.
53
+ */
54
+ private static buildKeywordMap(
55
+ config: MetaModelConfiguration
56
+ ): Map<string, { variantName: string; variant: MetaModelVariant }> {
57
+ const keywordMap = new Map<string, { variantName: string; variant: MetaModelVariant }>();
58
+
59
+ for (const [variantName, variant] of Object.entries(config.variants)) {
60
+ if (variant.keywords) {
61
+ for (const keyword of variant.keywords) {
62
+ const normalizedKeyword = keyword.toLowerCase();
63
+ keywordMap.set(normalizedKeyword, { variantName, variant });
64
+ }
65
+ }
66
+ }
67
+
68
+ return keywordMap;
69
+ }
70
+
71
+ /**
72
+ * Find all matching keywords at the start of a message.
73
+ * Returns keywords in the order they appear, for proper stripping.
74
+ */
75
+ private static findMatchingKeywords(
76
+ message: string,
77
+ keywordMap: Map<string, { variantName: string; variant: MetaModelVariant }>
78
+ ): Array<{ keyword: string; variantName: string; variant: MetaModelVariant; position: number }> {
79
+ const matches: Array<{
80
+ keyword: string;
81
+ variantName: string;
82
+ variant: MetaModelVariant;
83
+ position: number;
84
+ }> = [];
85
+
86
+ // Get all keywords, sorted by length (longest first for proper matching)
87
+ const keywords = Array.from(keywordMap.keys()).sort((a, b) => b.length - a.length);
88
+
89
+ // Check the beginning of the message for keywords
90
+ // Keywords must be at the start or preceded by whitespace
91
+ const messageLower = message.toLowerCase();
92
+
93
+ for (const keyword of keywords) {
94
+ // Check if keyword is at the start
95
+ if (messageLower.startsWith(keyword)) {
96
+ const afterKeyword = message.charAt(keyword.length);
97
+ // Keyword must be followed by whitespace or end of string
98
+ if (!afterKeyword || /\s/.test(afterKeyword)) {
99
+ const data = keywordMap.get(keyword);
100
+ if (data) {
101
+ matches.push({
102
+ keyword,
103
+ variantName: data.variantName,
104
+ variant: data.variant,
105
+ position: 0,
106
+ });
107
+ }
108
+ }
109
+ }
110
+
111
+ // Also check for keywords after initial whitespace (e.g., " ultrathink ...")
112
+ const leadingWhitespaceMatch = messageLower.match(/^\s+/);
113
+ if (leadingWhitespaceMatch) {
114
+ const offset = leadingWhitespaceMatch[0].length;
115
+ const restLower = messageLower.substring(offset);
116
+ if (restLower.startsWith(keyword)) {
117
+ const afterKeyword = message.charAt(offset + keyword.length);
118
+ if (!afterKeyword || /\s/.test(afterKeyword)) {
119
+ const data = keywordMap.get(keyword);
120
+ if (data && !matches.some((m) => m.keyword === keyword)) {
121
+ matches.push({
122
+ keyword,
123
+ variantName: data.variantName,
124
+ variant: data.variant,
125
+ position: offset,
126
+ });
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ return matches;
134
+ }
135
+
136
+ /**
137
+ * Select the winning variant from a set of matches using tier-based resolution.
138
+ * Highest tier wins. If tiers are equal, first match wins.
139
+ */
140
+ private static selectWinningVariant(
141
+ matches: Array<{ keyword: string; variantName: string; variant: MetaModelVariant; position: number }>
142
+ ): { keyword: string; variantName: string; variant: MetaModelVariant; position: number } | null {
143
+ if (matches.length === 0) {
144
+ return null;
145
+ }
146
+
147
+ // Sort by tier (descending), then by position (ascending for first match)
148
+ const sorted = [...matches].sort((a, b) => {
149
+ const tierA = a.variant.tier ?? 0;
150
+ const tierB = b.variant.tier ?? 0;
151
+ if (tierB !== tierA) {
152
+ return tierB - tierA; // Higher tier wins
153
+ }
154
+ return a.position - b.position; // Earlier position wins if tiers equal
155
+ });
156
+
157
+ return sorted[0];
158
+ }
159
+
160
+ /**
161
+ * Strip matched keywords from the beginning of a message.
162
+ * Preserves the rest of the message content.
163
+ */
164
+ private static stripKeywordsFromMessage(
165
+ message: string,
166
+ matchedKeywords: string[]
167
+ ): string {
168
+ let result = message;
169
+
170
+ for (const keyword of matchedKeywords) {
171
+ // Match the keyword at the start (case-insensitive) followed by optional whitespace
172
+ const regex = new RegExp(`^\\s*${escapeRegExp(keyword)}\\s*`, "i");
173
+ result = result.replace(regex, "");
174
+ }
175
+
176
+ return result.trim();
177
+ }
178
+
179
+ /**
180
+ * Resolve which variant to use based on the message content.
181
+ *
182
+ * @param config The meta model configuration
183
+ * @param message The user's message to analyze for keywords
184
+ * @param options Resolution options
185
+ * @returns The resolved variant information, or null if using default
186
+ */
187
+ static resolve(
188
+ config: MetaModelConfiguration,
189
+ message?: string,
190
+ options: ResolveOptions = {}
191
+ ): MetaModelResolution {
192
+ const { stripKeywords = true } = options;
193
+
194
+ // Build keyword map
195
+ const keywordMap = this.buildKeywordMap(config);
196
+
197
+ // If no message provided, use default
198
+ if (!message) {
199
+ const defaultVariant = config.variants[config.default];
200
+ if (!defaultVariant) {
201
+ throw new Error(
202
+ `Meta model default variant "${config.default}" not found in variants`
203
+ );
204
+ }
205
+ return {
206
+ variantName: config.default,
207
+ variant: defaultVariant,
208
+ configName: defaultVariant.model,
209
+ matchedKeywords: [],
210
+ systemPrompt: defaultVariant.systemPrompt,
211
+ };
212
+ }
213
+
214
+ // Find matching keywords
215
+ const matches = this.findMatchingKeywords(message, keywordMap);
216
+
217
+ // If no keywords matched, use default
218
+ if (matches.length === 0) {
219
+ const defaultVariant = config.variants[config.default];
220
+ if (!defaultVariant) {
221
+ throw new Error(
222
+ `Meta model default variant "${config.default}" not found in variants`
223
+ );
224
+ }
225
+ return {
226
+ variantName: config.default,
227
+ variant: defaultVariant,
228
+ configName: defaultVariant.model,
229
+ matchedKeywords: [],
230
+ systemPrompt: defaultVariant.systemPrompt,
231
+ };
232
+ }
233
+
234
+ // Select winning variant based on tier
235
+ const winner = this.selectWinningVariant(matches);
236
+ if (!winner) {
237
+ // Shouldn't happen if matches.length > 0, but handle gracefully
238
+ const defaultVariant = config.variants[config.default];
239
+ return {
240
+ variantName: config.default,
241
+ variant: defaultVariant,
242
+ configName: defaultVariant.model,
243
+ matchedKeywords: [],
244
+ systemPrompt: defaultVariant.systemPrompt,
245
+ };
246
+ }
247
+
248
+ // Collect all matched keywords (for logging/debugging)
249
+ const matchedKeywords = matches.map((m) => m.keyword);
250
+
251
+ // Strip keywords if requested
252
+ const strippedMessage = stripKeywords
253
+ ? this.stripKeywordsFromMessage(message, matchedKeywords)
254
+ : message;
255
+
256
+ logger.debug("[MetaModelResolver] Resolved variant", {
257
+ variantName: winner.variantName,
258
+ matchedKeywords,
259
+ tier: winner.variant.tier ?? 0,
260
+ configName: winner.variant.model,
261
+ });
262
+
263
+ return {
264
+ variantName: winner.variantName,
265
+ variant: winner.variant,
266
+ configName: winner.variant.model,
267
+ strippedMessage,
268
+ matchedKeywords,
269
+ systemPrompt: winner.variant.systemPrompt,
270
+ };
271
+ }
272
+
273
+ /**
274
+ * Resolve directly to a specific variant by name.
275
+ * Used when there's a variant override set (e.g., via change_model tool).
276
+ *
277
+ * @param config The meta model configuration
278
+ * @param variantName The name of the variant to resolve to
279
+ * @returns The resolved variant information
280
+ * @throws Error if the variant doesn't exist
281
+ */
282
+ static resolveToVariant(
283
+ config: MetaModelConfiguration,
284
+ variantName: string
285
+ ): MetaModelResolution {
286
+ const variant = config.variants[variantName];
287
+ if (!variant) {
288
+ throw new Error(
289
+ `Meta model variant "${variantName}" not found. Available variants: ${Object.keys(config.variants).join(", ")}`
290
+ );
291
+ }
292
+
293
+ logger.debug("[MetaModelResolver] Resolved to override variant", {
294
+ variantName,
295
+ configName: variant.model,
296
+ });
297
+
298
+ return {
299
+ variantName,
300
+ variant,
301
+ configName: variant.model,
302
+ matchedKeywords: [],
303
+ systemPrompt: variant.systemPrompt,
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Generate a system prompt fragment describing available variants.
309
+ * This helps the model understand what models are available and when to use them.
310
+ *
311
+ * @param config The meta model configuration
312
+ * @returns A system prompt fragment describing the variants
313
+ */
314
+ static generateSystemPromptFragment(config: MetaModelConfiguration): string {
315
+ const lines: string[] = [];
316
+
317
+ if (config.description) {
318
+ lines.push(config.description);
319
+ lines.push("");
320
+ }
321
+
322
+ lines.push("You have access to the following models via change_model() tool:");
323
+
324
+ for (const [variantName, variant] of Object.entries(config.variants)) {
325
+ const description = variant.description || `Model variant "${variantName}"`;
326
+ const keywords = variant.keywords?.length
327
+ ? ` (trigger: ${variant.keywords.join(", ")})`
328
+ : "";
329
+ lines.push(`* ${variantName}${keywords} → ${description}`);
330
+ }
331
+
332
+ return lines.join("\n");
333
+ }
334
+
335
+ /**
336
+ * Check if a configuration is a meta model configuration.
337
+ */
338
+ static isMetaModel(config: unknown): config is MetaModelConfiguration {
339
+ if (!config || typeof config !== "object") {
340
+ return false;
341
+ }
342
+ const c = config as Record<string, unknown>;
343
+ return c.provider === "meta" && typeof c.variants === "object" && typeof c.default === "string";
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Escape special regex characters in a string
349
+ */
350
+ function escapeRegExp(string: string): string {
351
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
352
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Meta Model Module
3
+ *
4
+ * Provides dynamic model selection capabilities through "meta models".
5
+ * Meta models are virtual configurations that resolve to real models
6
+ * based on keywords in user messages.
7
+ *
8
+ * @module
9
+ */
10
+
11
+ export { MetaModelResolver, type MetaModelResolution, type ResolveOptions } from "./MetaModelResolver";