@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.
- package/README.md +194 -0
- package/dist/backend-wrapper.cjs +3 -0
- package/dist/src/index.js +331928 -0
- package/package.json +103 -0
- package/src/agents/AgentRegistry.ts +418 -0
- package/src/agents/AgentStorage.ts +1133 -0
- package/src/agents/ConfigResolver.ts +229 -0
- package/src/agents/agent-installer.ts +236 -0
- package/src/agents/agent-loader.ts +241 -0
- package/src/agents/constants.ts +82 -0
- package/src/agents/errors.ts +48 -0
- package/src/agents/execution/AgentExecutor.ts +561 -0
- package/src/agents/execution/ExecutionContextFactory.ts +112 -0
- package/src/agents/execution/MessageCompiler.ts +597 -0
- package/src/agents/execution/MessageSyncer.ts +100 -0
- package/src/agents/execution/PostCompletionChecker.ts +278 -0
- package/src/agents/execution/ProgressMonitor.ts +50 -0
- package/src/agents/execution/RALResolver.ts +177 -0
- package/src/agents/execution/SessionManager.ts +181 -0
- package/src/agents/execution/StreamCallbacks.ts +312 -0
- package/src/agents/execution/StreamExecutionHandler.ts +579 -0
- package/src/agents/execution/StreamSetup.ts +313 -0
- package/src/agents/execution/ToolEventHandlers.ts +239 -0
- package/src/agents/execution/ToolExecutionTracker.ts +498 -0
- package/src/agents/execution/ToolResultUtils.ts +97 -0
- package/src/agents/execution/ToolSupervisionWrapper.ts +174 -0
- package/src/agents/execution/constants.ts +16 -0
- package/src/agents/execution/index.ts +3 -0
- package/src/agents/execution/types.ts +96 -0
- package/src/agents/execution/utils.ts +26 -0
- package/src/agents/index.ts +4 -0
- package/src/agents/script-installer.ts +266 -0
- package/src/agents/supervision/SupervisorLLMService.ts +253 -0
- package/src/agents/supervision/SupervisorOrchestrator.ts +471 -0
- package/src/agents/supervision/heuristics/ConsecutiveToolsWithoutTodoHeuristic.ts +73 -0
- package/src/agents/supervision/heuristics/DelegationClaimHeuristic.ts +80 -0
- package/src/agents/supervision/heuristics/HeuristicRegistry.ts +114 -0
- package/src/agents/supervision/heuristics/PendingTodosHeuristic.ts +93 -0
- package/src/agents/supervision/heuristics/SilentAgentHeuristic.ts +54 -0
- package/src/agents/supervision/heuristics/index.ts +5 -0
- package/src/agents/supervision/index.ts +28 -0
- package/src/agents/supervision/registerHeuristics.ts +110 -0
- package/src/agents/supervision/supervisionHealthCheck.ts +123 -0
- package/src/agents/supervision/types.ts +171 -0
- package/src/agents/tool-names.ts +46 -0
- package/src/agents/tool-normalization.ts +184 -0
- package/src/agents/types/index.ts +2 -0
- package/src/agents/types/runtime.ts +74 -0
- package/src/agents/types/storage.ts +145 -0
- package/src/commands/agent/import/index.ts +6 -0
- package/src/commands/agent/import/openclaw-distiller.ts +57 -0
- package/src/commands/agent/import/openclaw-reader.ts +141 -0
- package/src/commands/agent/import/openclaw.ts +154 -0
- package/src/commands/agent/index.ts +6 -0
- package/src/commands/agent.ts +215 -0
- package/src/commands/daemon.ts +198 -0
- package/src/commands/doctor.ts +134 -0
- package/src/commands/setup/embed.ts +228 -0
- package/src/commands/setup/global-system-prompt.ts +223 -0
- package/src/commands/setup/image.ts +179 -0
- package/src/commands/setup/index.ts +16 -0
- package/src/commands/setup/interactive.ts +95 -0
- package/src/commands/setup/llm.ts +38 -0
- package/src/commands/setup/onboarding.ts +294 -0
- package/src/commands/setup/providers.ts +27 -0
- package/src/constants.ts +34 -0
- package/src/conversations/ConversationDiskReader.ts +148 -0
- package/src/conversations/ConversationRegistry.ts +728 -0
- package/src/conversations/ConversationStore.ts +868 -0
- package/src/conversations/MessageBuilder.ts +866 -0
- package/src/conversations/executionTime.ts +62 -0
- package/src/conversations/formatters/DelegationXmlFormatter.ts +64 -0
- package/src/conversations/formatters/ThreadedConversationFormatter.ts +303 -0
- package/src/conversations/formatters/index.ts +9 -0
- package/src/conversations/formatters/utils/MessageFormatter.ts +46 -0
- package/src/conversations/formatters/utils/TimestampFormatter.ts +56 -0
- package/src/conversations/formatters/utils/TreeBuilder.ts +131 -0
- package/src/conversations/formatters/utils/TreeRenderer.ts +49 -0
- package/src/conversations/index.ts +2 -0
- package/src/conversations/persistence/ToolMessageStorage.ts +143 -0
- package/src/conversations/search/ConversationIndexManager.ts +393 -0
- package/src/conversations/search/QueryParser.ts +114 -0
- package/src/conversations/search/SearchEngine.ts +175 -0
- package/src/conversations/search/SnippetExtractor.ts +345 -0
- package/src/conversations/search/embeddings/ConversationEmbeddingService.ts +484 -0
- package/src/conversations/search/embeddings/ConversationIndexingJob.ts +320 -0
- package/src/conversations/search/embeddings/IndexingStateManager.ts +338 -0
- package/src/conversations/search/embeddings/index.ts +18 -0
- package/src/conversations/search/index.ts +49 -0
- package/src/conversations/search/types.ts +124 -0
- package/src/conversations/services/CategoryManager.ts +160 -0
- package/src/conversations/services/ConversationResolver.ts +296 -0
- package/src/conversations/services/ConversationSummarizer.ts +234 -0
- package/src/conversations/services/MetadataDebounceManager.ts +188 -0
- package/src/conversations/services/index.ts +2 -0
- package/src/conversations/types.ts +148 -0
- package/src/conversations/utils/content-utils.ts +69 -0
- package/src/conversations/utils/image-placeholder.ts +281 -0
- package/src/conversations/utils/image-url-utils.ts +171 -0
- package/src/conversations/utils/multimodal-content.ts +90 -0
- package/src/conversations/utils/tool-result-truncator.ts +159 -0
- package/src/daemon/Daemon.ts +1883 -0
- package/src/daemon/ProjectRuntime.ts +657 -0
- package/src/daemon/RestartState.ts +152 -0
- package/src/daemon/RuntimeLifecycle.ts +268 -0
- package/src/daemon/SubscriptionManager.ts +305 -0
- package/src/daemon/UnixSocketTransport.ts +318 -0
- package/src/daemon/filters/SubscriptionFilterBuilder.ts +119 -0
- package/src/daemon/index.ts +9 -0
- package/src/daemon/routing/DaemonRouter.ts +491 -0
- package/src/daemon/types.ts +150 -0
- package/src/daemon/utils/routing-log.ts +76 -0
- package/src/daemon/utils/telemetry.ts +173 -0
- package/src/event-handler/agentDeletion.ts +383 -0
- package/src/event-handler/index.ts +749 -0
- package/src/event-handler/newConversation.ts +165 -0
- package/src/event-handler/project.ts +166 -0
- package/src/event-handler/reply.ts +18 -0
- package/src/events/NDKAgentDefinition.ts +292 -0
- package/src/events/NDKAgentLesson.ts +106 -0
- package/src/events/NDKEventMetadata.ts +34 -0
- package/src/events/NDKMCPTool.ts +60 -0
- package/src/events/NDKProjectStatus.ts +384 -0
- package/src/events/index.ts +4 -0
- package/src/index.ts +126 -0
- package/src/lib/agent-home.ts +334 -0
- package/src/lib/error-formatter.ts +200 -0
- package/src/lib/fs/filesystem.ts +128 -0
- package/src/lib/fs/index.ts +1 -0
- package/src/lib/json-parser.ts +30 -0
- package/src/lib/string.ts +15 -0
- package/src/lib/time.ts +74 -0
- package/src/llm/ChunkHandler.ts +277 -0
- package/src/llm/FinishHandler.ts +250 -0
- package/src/llm/LLMConfigEditor.ts +154 -0
- package/src/llm/LLMServiceFactory.ts +230 -0
- package/src/llm/MessageProcessor.ts +90 -0
- package/src/llm/RecordingState.ts +37 -0
- package/src/llm/StreamPublisher.ts +40 -0
- package/src/llm/TracingUtils.ts +77 -0
- package/src/llm/chunk-validators.ts +57 -0
- package/src/llm/constants.ts +6 -0
- package/src/llm/index.ts +12 -0
- package/src/llm/meta/MetaModelResolver.ts +352 -0
- package/src/llm/meta/index.ts +11 -0
- package/src/llm/middleware/flight-recorder.ts +188 -0
- package/src/llm/providers/MockProvider.ts +332 -0
- package/src/llm/providers/agent/ClaudeCodeProvider.ts +343 -0
- package/src/llm/providers/agent/ClaudeCodeToolsAdapter.ts +203 -0
- package/src/llm/providers/agent/CodexAppServerProvider.ts +214 -0
- package/src/llm/providers/agent/CodexAppServerToolsAdapter.ts +91 -0
- package/src/llm/providers/agent/index.ts +10 -0
- package/src/llm/providers/base/AgentProvider.ts +107 -0
- package/src/llm/providers/base/BaseProvider.ts +114 -0
- package/src/llm/providers/base/StandardProvider.ts +38 -0
- package/src/llm/providers/base/index.ts +9 -0
- package/src/llm/providers/index.ts +106 -0
- package/src/llm/providers/key-manager.ts +238 -0
- package/src/llm/providers/ollama-models.ts +105 -0
- package/src/llm/providers/openrouter-models.ts +102 -0
- package/src/llm/providers/provider-ids.ts +18 -0
- package/src/llm/providers/registry/ProviderRegistry.ts +414 -0
- package/src/llm/providers/registry/index.ts +7 -0
- package/src/llm/providers/standard/AnthropicProvider.ts +71 -0
- package/src/llm/providers/standard/OllamaProvider.ts +59 -0
- package/src/llm/providers/standard/OpenAIProvider.ts +44 -0
- package/src/llm/providers/standard/OpenRouterProvider.ts +103 -0
- package/src/llm/providers/standard/index.ts +10 -0
- package/src/llm/providers/types.ts +194 -0
- package/src/llm/providers/usage-metadata.ts +78 -0
- package/src/llm/service.ts +713 -0
- package/src/llm/types.ts +167 -0
- package/src/llm/utils/ConfigurationManager.ts +650 -0
- package/src/llm/utils/ConfigurationTester.ts +229 -0
- package/src/llm/utils/ModelSelector.ts +212 -0
- package/src/llm/utils/ProviderConfigUI.ts +177 -0
- package/src/llm/utils/claudeCodePromptCompiler.ts +141 -0
- package/src/llm/utils/codex-models.ts +53 -0
- package/src/llm/utils/context-window-cache.ts +30 -0
- package/src/llm/utils/models-dev-cache.ts +267 -0
- package/src/llm/utils/provider-setup.ts +50 -0
- package/src/llm/utils/tool-errors.ts +78 -0
- package/src/llm/utils/usage.ts +74 -0
- package/src/logging/EventRoutingLogger.ts +205 -0
- package/src/nostr/AgentEventDecoder.ts +357 -0
- package/src/nostr/AgentEventEncoder.ts +677 -0
- package/src/nostr/AgentProfilePublisher.ts +657 -0
- package/src/nostr/AgentPublisher.ts +437 -0
- package/src/nostr/BlossomService.ts +226 -0
- package/src/nostr/InterventionPublisher.ts +132 -0
- package/src/nostr/TagExtractor.ts +228 -0
- package/src/nostr/collectEvents.ts +83 -0
- package/src/nostr/constants.ts +38 -0
- package/src/nostr/encryption.ts +26 -0
- package/src/nostr/index.ts +31 -0
- package/src/nostr/keys.ts +17 -0
- package/src/nostr/kinds.ts +37 -0
- package/src/nostr/ndkClient.ts +72 -0
- package/src/nostr/relays.ts +43 -0
- package/src/nostr/trace-context.ts +39 -0
- package/src/nostr/types.ts +227 -0
- package/src/nostr/utils.ts +84 -0
- package/src/prompts/core/FragmentRegistry.ts +30 -0
- package/src/prompts/core/PromptBuilder.ts +98 -0
- package/src/prompts/core/index.ts +3 -0
- package/src/prompts/core/types.ts +13 -0
- package/src/prompts/fragments/00-global-system-prompt.ts +44 -0
- package/src/prompts/fragments/01-agent-identity.ts +69 -0
- package/src/prompts/fragments/02-agent-home-directory.ts +114 -0
- package/src/prompts/fragments/03-system-reminders-explanation.ts +14 -0
- package/src/prompts/fragments/04-relay-configuration.ts +38 -0
- package/src/prompts/fragments/05-delegation-chain.ts +45 -0
- package/src/prompts/fragments/06-agent-todos.ts +74 -0
- package/src/prompts/fragments/06-todo-usage-guidance.ts +34 -0
- package/src/prompts/fragments/07-meta-project-context.ts +234 -0
- package/src/prompts/fragments/08-active-conversations.ts +382 -0
- package/src/prompts/fragments/09-recent-conversations.ts +153 -0
- package/src/prompts/fragments/10-referenced-article.ts +21 -0
- package/src/prompts/fragments/11-nudges.ts +134 -0
- package/src/prompts/fragments/12-skills.ts +127 -0
- package/src/prompts/fragments/13-available-nudges.ts +122 -0
- package/src/prompts/fragments/15-available-agents.ts +53 -0
- package/src/prompts/fragments/16-stay-in-your-lane.ts +41 -0
- package/src/prompts/fragments/17-todo-before-delegation.ts +39 -0
- package/src/prompts/fragments/20-voice-mode.ts +62 -0
- package/src/prompts/fragments/22-scheduled-tasks.ts +175 -0
- package/src/prompts/fragments/24-retrieved-lessons.ts +26 -0
- package/src/prompts/fragments/25-rag-instructions.ts +333 -0
- package/src/prompts/fragments/26-mcp-resources.ts +237 -0
- package/src/prompts/fragments/27-memorized-reports.ts +77 -0
- package/src/prompts/fragments/28-agent-directed-monitoring.ts +32 -0
- package/src/prompts/fragments/29-rag-collections.ts +50 -0
- package/src/prompts/fragments/30-worktree-context.ts +98 -0
- package/src/prompts/fragments/31-agents-md-guidance.ts +96 -0
- package/src/prompts/fragments/32-process-metrics.ts +72 -0
- package/src/prompts/fragments/debug-mode.ts +48 -0
- package/src/prompts/fragments/delegation-completion.ts +44 -0
- package/src/prompts/fragments/index.ts +91 -0
- package/src/prompts/index.ts +21 -0
- package/src/prompts/utils/systemPromptBuilder.ts +777 -0
- package/src/scripts/migrate-prefix-index.ts +157 -0
- package/src/services/AgentDefinitionMonitor.ts +701 -0
- package/src/services/ConfigService.ts +723 -0
- package/src/services/CooldownRegistry.ts +199 -0
- package/src/services/LLMOperationsRegistry.ts +424 -0
- package/src/services/OwnerAgentListService.ts +354 -0
- package/src/services/PubkeyService.ts +308 -0
- package/src/services/agents/AgentMetadataStore.ts +72 -0
- package/src/services/agents/AgentResolution.ts +59 -0
- package/src/services/agents/EscalationService.ts +281 -0
- package/src/services/agents/NDKAgentDiscovery.ts +95 -0
- package/src/services/agents/index.ts +7 -0
- package/src/services/agents-md/AgentsMdService.ts +184 -0
- package/src/services/agents-md/SystemReminderInjector.ts +238 -0
- package/src/services/agents-md/index.ts +11 -0
- package/src/services/apns/APNsClient.ts +203 -0
- package/src/services/apns/APNsService.ts +358 -0
- package/src/services/apns/index.ts +11 -0
- package/src/services/apns/types.ts +80 -0
- package/src/services/compression/CompressionService.ts +445 -0
- package/src/services/compression/compression-schema.ts +28 -0
- package/src/services/compression/compression-types.ts +74 -0
- package/src/services/compression/compression-utils.ts +587 -0
- package/src/services/config/types.ts +394 -0
- package/src/services/dispatch/AgentDispatchService.ts +937 -0
- package/src/services/dispatch/AgentRouter.ts +181 -0
- package/src/services/dispatch/DelegationCompletionHandler.ts +232 -0
- package/src/services/embedding/EmbeddingProvider.ts +188 -0
- package/src/services/embedding/index.ts +5 -0
- package/src/services/event-context/EventContextService.ts +108 -0
- package/src/services/event-context/index.ts +2 -0
- package/src/services/heuristics/ContextBuilder.ts +106 -0
- package/src/services/heuristics/HeuristicEngine.ts +200 -0
- package/src/services/heuristics/formatters.ts +58 -0
- package/src/services/heuristics/index.ts +12 -0
- package/src/services/heuristics/rules/index.ts +25 -0
- package/src/services/heuristics/rules/todoBeforeDelegation.ts +69 -0
- package/src/services/heuristics/rules/todoReminderOnToolUse.ts +63 -0
- package/src/services/heuristics/types.ts +144 -0
- package/src/services/image/ImageGenerationService.ts +389 -0
- package/src/services/image/index.ts +12 -0
- package/src/services/intervention/InterventionService.ts +1352 -0
- package/src/services/intervention/index.ts +7 -0
- package/src/services/mcp/MCPManager.ts +683 -0
- package/src/services/mcp/McpNotificationDelivery.ts +139 -0
- package/src/services/mcp/McpSubscriptionService.ts +653 -0
- package/src/services/mcp/mcpInstaller.ts +130 -0
- package/src/services/nip46/Nip46SigningLog.ts +81 -0
- package/src/services/nip46/Nip46SigningService.ts +467 -0
- package/src/services/nip46/index.ts +4 -0
- package/src/services/nudge/NudgeService.ts +224 -0
- package/src/services/nudge/NudgeWhitelistService.ts +382 -0
- package/src/services/nudge/index.ts +5 -0
- package/src/services/nudge/types.ts +83 -0
- package/src/services/projects/ProjectContext.ts +672 -0
- package/src/services/projects/ProjectContextStore.ts +102 -0
- package/src/services/projects/index.ts +6 -0
- package/src/services/prompt-compiler/index.ts +15 -0
- package/src/services/prompt-compiler/prompt-compiler-service.ts +1143 -0
- package/src/services/pubkey-gate/PubkeyGateService.ts +93 -0
- package/src/services/pubkey-gate/index.ts +1 -0
- package/src/services/rag/EmbeddingProviderFactory.ts +292 -0
- package/src/services/rag/LanceDBMaintenanceService.ts +211 -0
- package/src/services/rag/RAGDatabaseService.ts +173 -0
- package/src/services/rag/RAGOperations.ts +682 -0
- package/src/services/rag/RAGService.ts +240 -0
- package/src/services/rag/RagSubscriptionService.ts +618 -0
- package/src/services/rag/rag-utils.ts +174 -0
- package/src/services/ral/PendingDelegationsRegistry.ts +168 -0
- package/src/services/ral/RALRegistry.ts +2782 -0
- package/src/services/ral/index.ts +4 -0
- package/src/services/ral/types.ts +292 -0
- package/src/services/reports/LocalReportStore.ts +380 -0
- package/src/services/reports/ReportEmbeddingService.ts +430 -0
- package/src/services/reports/ReportService.ts +440 -0
- package/src/services/reports/articleUtils.ts +52 -0
- package/src/services/reports/index.ts +7 -0
- package/src/services/scheduling/SchedulerService.ts +1057 -0
- package/src/services/scheduling/errors.ts +14 -0
- package/src/services/scheduling/index.ts +7 -0
- package/src/services/scheduling/utils.ts +77 -0
- package/src/services/search/SearchProviderRegistry.ts +78 -0
- package/src/services/search/UnifiedSearchService.ts +218 -0
- package/src/services/search/index.ts +47 -0
- package/src/services/search/projectFilter.ts +22 -0
- package/src/services/search/providers/ConversationSearchProvider.ts +48 -0
- package/src/services/search/providers/LessonSearchProvider.ts +75 -0
- package/src/services/search/providers/ReportSearchProvider.ts +49 -0
- package/src/services/search/types.ts +144 -0
- package/src/services/skill/SkillService.ts +482 -0
- package/src/services/skill/index.ts +2 -0
- package/src/services/skill/types.ts +70 -0
- package/src/services/status/OperationsStatusService.ts +276 -0
- package/src/services/status/ProjectStatusService.ts +522 -0
- package/src/services/status/index.ts +11 -0
- package/src/services/storage/PrefixKVStore.ts +242 -0
- package/src/services/storage/index.ts +1 -0
- package/src/services/system-reminder/SystemReminderUtils.ts +96 -0
- package/src/services/system-reminder/index.ts +7 -0
- package/src/services/trust-pubkeys/TrustPubkeyService.ts +325 -0
- package/src/services/trust-pubkeys/index.ts +2 -0
- package/src/telemetry/ConversationSpanManager.ts +111 -0
- package/src/telemetry/EventLoopMonitor.ts +206 -0
- package/src/telemetry/LLMSpanRegistry.ts +20 -0
- package/src/telemetry/NostrSpanProcessor.ts +89 -0
- package/src/telemetry/ToolCallSpanProcessor.ts +66 -0
- package/src/telemetry/diagnostics.ts +27 -0
- package/src/telemetry/setup.ts +120 -0
- package/src/tools/implementations/agents_discover.ts +121 -0
- package/src/tools/implementations/agents_hire.ts +127 -0
- package/src/tools/implementations/agents_list.ts +96 -0
- package/src/tools/implementations/agents_publish.ts +611 -0
- package/src/tools/implementations/agents_read.ts +173 -0
- package/src/tools/implementations/agents_write.ts +200 -0
- package/src/tools/implementations/ask.ts +411 -0
- package/src/tools/implementations/change_model.ts +141 -0
- package/src/tools/implementations/conversation_get.ts +661 -0
- package/src/tools/implementations/conversation_list.ts +377 -0
- package/src/tools/implementations/conversation_search.ts +370 -0
- package/src/tools/implementations/delegate.ts +327 -0
- package/src/tools/implementations/delegate_crossproject.ts +209 -0
- package/src/tools/implementations/delegate_followup.ts +300 -0
- package/src/tools/implementations/fs_edit.ts +162 -0
- package/src/tools/implementations/fs_glob.ts +182 -0
- package/src/tools/implementations/fs_grep.ts +513 -0
- package/src/tools/implementations/fs_read.ts +332 -0
- package/src/tools/implementations/fs_write.ts +113 -0
- package/src/tools/implementations/generate_image.ts +259 -0
- package/src/tools/implementations/home_fs.ts +515 -0
- package/src/tools/implementations/kill.ts +651 -0
- package/src/tools/implementations/learn.ts +166 -0
- package/src/tools/implementations/lesson-formatter.ts +38 -0
- package/src/tools/implementations/lesson_delete.ts +164 -0
- package/src/tools/implementations/lesson_get.ts +105 -0
- package/src/tools/implementations/lessons_list.ts +153 -0
- package/src/tools/implementations/mcp_resource_read.ts +161 -0
- package/src/tools/implementations/mcp_subscribe.ts +158 -0
- package/src/tools/implementations/mcp_subscription_stop.ts +85 -0
- package/src/tools/implementations/nostr_fetch.ts +149 -0
- package/src/tools/implementations/nostr_publish_as_user.ts +353 -0
- package/src/tools/implementations/project_list.ts +146 -0
- package/src/tools/implementations/rag_add_documents.ts +573 -0
- package/src/tools/implementations/rag_create_collection.ts +65 -0
- package/src/tools/implementations/rag_delete_collection.ts +68 -0
- package/src/tools/implementations/rag_list_collections.ts +77 -0
- package/src/tools/implementations/rag_query.ts +107 -0
- package/src/tools/implementations/rag_subscription_create.ts +105 -0
- package/src/tools/implementations/rag_subscription_delete.ts +80 -0
- package/src/tools/implementations/rag_subscription_get.ts +123 -0
- package/src/tools/implementations/rag_subscription_list.ts +128 -0
- package/src/tools/implementations/report_delete.ts +79 -0
- package/src/tools/implementations/report_read.ts +160 -0
- package/src/tools/implementations/report_write.ts +278 -0
- package/src/tools/implementations/reports_list.ts +77 -0
- package/src/tools/implementations/schedule_task.ts +104 -0
- package/src/tools/implementations/schedule_task_cancel.ts +62 -0
- package/src/tools/implementations/schedule_task_once.ts +128 -0
- package/src/tools/implementations/schedule_tasks_list.ts +79 -0
- package/src/tools/implementations/search.ts +160 -0
- package/src/tools/implementations/shell.ts +553 -0
- package/src/tools/implementations/todo.ts +260 -0
- package/src/tools/implementations/upload_blob.ts +381 -0
- package/src/tools/implementations/web_fetch.ts +153 -0
- package/src/tools/implementations/web_search.ts +250 -0
- package/src/tools/registry.ts +670 -0
- package/src/tools/types.ts +177 -0
- package/src/tools/utils.ts +256 -0
- package/src/types/event-ids.ts +320 -0
- package/src/types/index.ts +46 -0
- package/src/utils/agentFetcher.ts +107 -0
- package/src/utils/cli-error.ts +29 -0
- package/src/utils/conversation-id.ts +27 -0
- package/src/utils/conversation-utils.ts +1 -0
- package/src/utils/delegation-chain.ts +357 -0
- package/src/utils/error-handler.ts +42 -0
- package/src/utils/git/gitignore.ts +69 -0
- package/src/utils/git/index.ts +2 -0
- package/src/utils/git/initializeGitRepo.ts +204 -0
- package/src/utils/git/worktree.ts +260 -0
- package/src/utils/lessonFormatter.ts +70 -0
- package/src/utils/lessonTrust.ts +24 -0
- package/src/utils/lockfile.ts +123 -0
- package/src/utils/logger.ts +149 -0
- package/src/utils/nostr-entity-parser.ts +365 -0
- package/src/utils/process.ts +49 -0
- package/src/wrapper.ts +262 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { CONFIG_FILE, LLMS_FILE, MCP_CONFIG_FILE, PROVIDERS_FILE, TENEX_DIR, getTenexBasePath } from "@/constants";
|
|
4
|
+
import { ensureDirectory, fileExists, readJsonFile, writeJsonFile } from "@/lib/fs";
|
|
5
|
+
import { llmServiceFactory } from "@/llm/LLMServiceFactory";
|
|
6
|
+
import { MetaModelResolver } from "@/llm/meta";
|
|
7
|
+
import { ensureCacheLoaded as ensureModelsDevCacheLoaded } from "@/llm/utils/models-dev-cache";
|
|
8
|
+
import type { MCPConfig } from "@/llm/providers/types";
|
|
9
|
+
import type { LLMService } from "@/llm/service";
|
|
10
|
+
import type { OnStreamStartCallback } from "@/llm/types";
|
|
11
|
+
import type {
|
|
12
|
+
ConfigFile,
|
|
13
|
+
LLMConfiguration,
|
|
14
|
+
LoadedConfig,
|
|
15
|
+
MetaModelConfiguration,
|
|
16
|
+
TenexConfig,
|
|
17
|
+
TenexLLMs,
|
|
18
|
+
TenexMCP,
|
|
19
|
+
TenexProviders,
|
|
20
|
+
} from "@/services/config/types";
|
|
21
|
+
import { isMetaModelConfiguration, TenexConfigSchema, TenexLLMsSchema, TenexMCPSchema, TenexProvidersSchema } from "@/services/config/types";
|
|
22
|
+
import { formatAnyError } from "@/lib/error-formatter";
|
|
23
|
+
import { logger } from "@/utils/logger";
|
|
24
|
+
import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
|
25
|
+
import type { z } from "zod";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Result of resolving a meta model configuration
|
|
29
|
+
*/
|
|
30
|
+
export interface MetaModelResolutionResult {
|
|
31
|
+
/** The resolved LLM configuration to use */
|
|
32
|
+
config: LLMConfiguration;
|
|
33
|
+
/** The resolved configuration name */
|
|
34
|
+
configName: string;
|
|
35
|
+
/** The message with keywords stripped (if applicable) */
|
|
36
|
+
strippedMessage?: string;
|
|
37
|
+
/** Additional system prompt to inject from the variant */
|
|
38
|
+
variantSystemPrompt?: string;
|
|
39
|
+
/** System prompt fragment describing available variants */
|
|
40
|
+
metaModelSystemPrompt?: string;
|
|
41
|
+
/** Whether this was a meta model resolution */
|
|
42
|
+
isMetaModel: boolean;
|
|
43
|
+
/** The variant name that was selected (if meta model) */
|
|
44
|
+
variantName?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Subdirectory paths under ~/.tenex
|
|
49
|
+
*/
|
|
50
|
+
export type TenexSubdir =
|
|
51
|
+
| "agents"
|
|
52
|
+
| "daemon"
|
|
53
|
+
| "conversations"
|
|
54
|
+
| "data"
|
|
55
|
+
| "projects"
|
|
56
|
+
| "tool-messages";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Centralized configuration service for TENEX
|
|
60
|
+
* Handles loading and saving of all configuration files
|
|
61
|
+
* All configurations are stored in ~/.tenex
|
|
62
|
+
*/
|
|
63
|
+
export class ConfigService {
|
|
64
|
+
private cache = new Map<string, { data: unknown; timestamp: number }>();
|
|
65
|
+
private loadedConfig?: LoadedConfig;
|
|
66
|
+
|
|
67
|
+
// =====================================================================================
|
|
68
|
+
// PATH UTILITIES
|
|
69
|
+
// =====================================================================================
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get a path under the global TENEX directory (defaults to ~/.tenex)
|
|
73
|
+
* Respects TENEX_BASE_DIR environment variable for instance isolation.
|
|
74
|
+
* @param subdir Optional subdirectory (e.g., "agents", "daemon")
|
|
75
|
+
* @returns Full path to the directory or subdirectory
|
|
76
|
+
*/
|
|
77
|
+
getConfigPath(subdir?: TenexSubdir | string): string {
|
|
78
|
+
const basePath = getTenexBasePath();
|
|
79
|
+
return subdir ? path.join(basePath, subdir) : basePath;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getGlobalPath(): string {
|
|
83
|
+
return this.getConfigPath();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getProjectPath(projectPath: string): string {
|
|
87
|
+
return path.join(projectPath, TENEX_DIR);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the base directory for all projects
|
|
92
|
+
* Defaults to ~/tenex if not configured
|
|
93
|
+
*/
|
|
94
|
+
getProjectsBase(): string {
|
|
95
|
+
const config = this.loadedConfig?.config;
|
|
96
|
+
return config?.projectsBase
|
|
97
|
+
? path.resolve(config.projectsBase)
|
|
98
|
+
: path.join(homedir(), "tenex");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private getConfigFilePath(basePath: string, configFile: ConfigFile): string {
|
|
102
|
+
return path.join(basePath, configFile);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// =====================================================================================
|
|
106
|
+
// COMPLETE CONFIGURATION LOADING
|
|
107
|
+
// =====================================================================================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get the currently loaded config
|
|
111
|
+
*/
|
|
112
|
+
getConfig(): TenexConfig {
|
|
113
|
+
if (!this.loadedConfig) {
|
|
114
|
+
throw new Error("Config not loaded. Call loadConfig() first.");
|
|
115
|
+
}
|
|
116
|
+
return this.loadedConfig.config;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getMCP(): TenexMCP {
|
|
120
|
+
if (!this.loadedConfig) {
|
|
121
|
+
throw new Error("Config not loaded. Call loadConfig() first.");
|
|
122
|
+
}
|
|
123
|
+
return this.loadedConfig.mcp;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Load all TENEX configuration
|
|
128
|
+
* @param metadataPath Optional project metadata path (~/.tenex/projects/{dTag}) for project MCP config
|
|
129
|
+
*/
|
|
130
|
+
async loadConfig(metadataPath?: string): Promise<LoadedConfig> {
|
|
131
|
+
const globalPath = this.getGlobalPath();
|
|
132
|
+
|
|
133
|
+
// Load global config only (no project-level config.json)
|
|
134
|
+
const config = await this.loadTenexConfig(globalPath);
|
|
135
|
+
|
|
136
|
+
// Load providers from providers.json
|
|
137
|
+
const providers = await this.loadTenexProviders(globalPath);
|
|
138
|
+
|
|
139
|
+
// Load global LLMs only (no project-level llms.json)
|
|
140
|
+
const llms = await this.loadTenexLLMs(globalPath);
|
|
141
|
+
|
|
142
|
+
// Validate provider references
|
|
143
|
+
this.validateProviderReferences(llms, providers);
|
|
144
|
+
|
|
145
|
+
// Load MCP (merge global and project metadata path)
|
|
146
|
+
const globalMCP = await this.loadTenexMCP(globalPath);
|
|
147
|
+
const projectMCP = metadataPath
|
|
148
|
+
? await this.loadTenexMCP(metadataPath)
|
|
149
|
+
: { servers: {}, enabled: true };
|
|
150
|
+
const mcp: TenexMCP = {
|
|
151
|
+
servers: { ...globalMCP.servers, ...projectMCP.servers },
|
|
152
|
+
enabled: projectMCP.enabled !== undefined ? projectMCP.enabled : globalMCP.enabled,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const loadedConfig = { config, llms, mcp, providers };
|
|
156
|
+
this.loadedConfig = loadedConfig;
|
|
157
|
+
|
|
158
|
+
// Initialize the LLM factory with provider configs and global settings
|
|
159
|
+
await llmServiceFactory.initializeProviders(providers.providers);
|
|
160
|
+
|
|
161
|
+
// Load models.dev cache for model metadata (context windows, output limits)
|
|
162
|
+
await ensureModelsDevCacheLoaded();
|
|
163
|
+
|
|
164
|
+
return loadedConfig;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// =====================================================================================
|
|
168
|
+
// INDIVIDUAL FILE LOADING
|
|
169
|
+
// =====================================================================================
|
|
170
|
+
|
|
171
|
+
async loadTenexConfig(basePath: string): Promise<TenexConfig> {
|
|
172
|
+
return this.loadConfigFile(
|
|
173
|
+
this.getConfigFilePath(basePath, CONFIG_FILE),
|
|
174
|
+
TenexConfigSchema,
|
|
175
|
+
{}
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async loadTenexLLMs(basePath: string): Promise<TenexLLMs> {
|
|
180
|
+
return this.loadConfigFile(this.getConfigFilePath(basePath, LLMS_FILE), TenexLLMsSchema, {
|
|
181
|
+
configurations: {},
|
|
182
|
+
default: undefined,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async loadTenexMCP(basePath: string): Promise<TenexMCP> {
|
|
187
|
+
const result = await this.loadConfigFile(
|
|
188
|
+
this.getConfigFilePath(basePath, MCP_CONFIG_FILE),
|
|
189
|
+
TenexMCPSchema,
|
|
190
|
+
{
|
|
191
|
+
servers: {},
|
|
192
|
+
enabled: true,
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
// Ensure servers is always defined
|
|
196
|
+
return {
|
|
197
|
+
servers: result.servers || {},
|
|
198
|
+
enabled: result.enabled ?? true,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async loadTenexProviders(basePath: string): Promise<TenexProviders> {
|
|
203
|
+
return this.loadConfigFile(
|
|
204
|
+
this.getConfigFilePath(basePath, PROVIDERS_FILE),
|
|
205
|
+
TenexProvidersSchema,
|
|
206
|
+
{ providers: {} }
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// =====================================================================================
|
|
211
|
+
// INDIVIDUAL FILE SAVING
|
|
212
|
+
// =====================================================================================
|
|
213
|
+
|
|
214
|
+
async saveTenexConfig(basePath: string, config: TenexConfig): Promise<void> {
|
|
215
|
+
await this.saveConfigFile(
|
|
216
|
+
this.getConfigFilePath(basePath, CONFIG_FILE),
|
|
217
|
+
config,
|
|
218
|
+
TenexConfigSchema
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async saveTenexLLMs(basePath: string, llms: TenexLLMs): Promise<void> {
|
|
223
|
+
await this.saveConfigFile(
|
|
224
|
+
this.getConfigFilePath(basePath, LLMS_FILE),
|
|
225
|
+
llms,
|
|
226
|
+
TenexLLMsSchema
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async saveTenexMCP(basePath: string, mcp: TenexMCP): Promise<void> {
|
|
231
|
+
await this.saveConfigFile(
|
|
232
|
+
this.getConfigFilePath(basePath, MCP_CONFIG_FILE),
|
|
233
|
+
mcp,
|
|
234
|
+
TenexMCPSchema
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async saveTenexProviders(basePath: string, providers: TenexProviders): Promise<void> {
|
|
239
|
+
await this.saveConfigFile(
|
|
240
|
+
this.getConfigFilePath(basePath, PROVIDERS_FILE),
|
|
241
|
+
providers,
|
|
242
|
+
TenexProvidersSchema
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// =====================================================================================
|
|
247
|
+
// LLM SERVICE CREATION
|
|
248
|
+
// =====================================================================================
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Resolve a configuration name to an actual name, handling defaults and fallbacks.
|
|
252
|
+
* This is the single source of truth for config name resolution logic.
|
|
253
|
+
*
|
|
254
|
+
* @param configName - The requested configuration name (may be undefined or "default")
|
|
255
|
+
* @param options - Resolution options
|
|
256
|
+
* @returns Object with resolved name and optional warning message
|
|
257
|
+
*/
|
|
258
|
+
private resolveConfigName(
|
|
259
|
+
configName: string | undefined,
|
|
260
|
+
options: { allowFallback?: boolean; warnOnFallback?: boolean } = {}
|
|
261
|
+
): { name: string; warning?: string } {
|
|
262
|
+
const { allowFallback = true, warnOnFallback = true } = options;
|
|
263
|
+
|
|
264
|
+
if (!this.loadedConfig) {
|
|
265
|
+
throw new Error("Config not loaded. Call loadConfig() first.");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const llms = this.loadedConfig.llms;
|
|
269
|
+
const available = Object.keys(llms.configurations);
|
|
270
|
+
|
|
271
|
+
// If configName is "default" or not provided, use the actual default from config
|
|
272
|
+
let name = configName;
|
|
273
|
+
if (!name || name === "default") {
|
|
274
|
+
name = llms.default;
|
|
275
|
+
if (!name) {
|
|
276
|
+
if (available.length > 0) {
|
|
277
|
+
name = available[0];
|
|
278
|
+
if (warnOnFallback) {
|
|
279
|
+
return { name, warning: `No default LLM configured, using first available: ${name}` };
|
|
280
|
+
}
|
|
281
|
+
return { name };
|
|
282
|
+
}
|
|
283
|
+
throw new Error("No LLM configurations available");
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Check if the requested config exists
|
|
288
|
+
if (llms.configurations[name]) {
|
|
289
|
+
return { name };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Config not found - try fallback if allowed
|
|
293
|
+
if (!allowFallback) {
|
|
294
|
+
throw new Error(`LLM configuration "${name}" not found`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Try default
|
|
298
|
+
const defaultName = llms.default;
|
|
299
|
+
if (defaultName && llms.configurations[defaultName]) {
|
|
300
|
+
const warning = warnOnFallback
|
|
301
|
+
? `LLM configuration "${name}" not found, falling back to default: ${defaultName}`
|
|
302
|
+
: undefined;
|
|
303
|
+
return { name: defaultName, warning };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Try first available
|
|
307
|
+
if (available.length > 0) {
|
|
308
|
+
const fallbackName = available[0];
|
|
309
|
+
const warning = warnOnFallback
|
|
310
|
+
? `LLM configuration "${name}" not found, using first available: ${fallbackName}`
|
|
311
|
+
: undefined;
|
|
312
|
+
return { name: fallbackName, warning };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
throw new Error(
|
|
316
|
+
`No valid LLM configuration found. Requested: "${configName || "default"}". ` +
|
|
317
|
+
`Available: ${available.length > 0 ? available.join(", ") : "none"}`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Get LLM configuration by name.
|
|
323
|
+
* If the configuration is a meta model, automatically resolves to the default variant.
|
|
324
|
+
* Use resolveMetaModel() for keyword-based variant selection.
|
|
325
|
+
*/
|
|
326
|
+
getLLMConfig(configName?: string): LLMConfiguration {
|
|
327
|
+
const { name, warning } = this.resolveConfigName(configName, {
|
|
328
|
+
allowFallback: true,
|
|
329
|
+
warnOnFallback: true,
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
if (warning) {
|
|
333
|
+
logger.warn(warning);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const loadedConfig = this.loadedConfig;
|
|
337
|
+
if (!loadedConfig) {
|
|
338
|
+
throw new Error("Config not loaded. Call loadConfig() first.");
|
|
339
|
+
}
|
|
340
|
+
const config = loadedConfig.llms.configurations[name];
|
|
341
|
+
|
|
342
|
+
// If it's a meta model, resolve to the default variant
|
|
343
|
+
if (isMetaModelConfiguration(config)) {
|
|
344
|
+
const resolution = MetaModelResolver.resolve(config);
|
|
345
|
+
// Recursively get the underlying config
|
|
346
|
+
return this.getLLMConfig(resolution.configName);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return config;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Get raw LLM configuration by name without type coercion.
|
|
354
|
+
* Returns the configuration as stored, which may be either a standard
|
|
355
|
+
* LLMConfiguration or a MetaModelConfiguration.
|
|
356
|
+
*/
|
|
357
|
+
getRawLLMConfig(configName?: string): LLMConfiguration | MetaModelConfiguration {
|
|
358
|
+
const { name } = this.resolveConfigName(configName, {
|
|
359
|
+
allowFallback: false,
|
|
360
|
+
warnOnFallback: false,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const loadedConfig = this.loadedConfig;
|
|
364
|
+
if (!loadedConfig) {
|
|
365
|
+
throw new Error("Config not loaded. Call loadConfig() first.");
|
|
366
|
+
}
|
|
367
|
+
return loadedConfig.llms.configurations[name];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Check if a configuration name refers to a meta model.
|
|
372
|
+
*/
|
|
373
|
+
isMetaModelConfig(configName?: string): boolean {
|
|
374
|
+
try {
|
|
375
|
+
const config = this.getRawLLMConfig(configName);
|
|
376
|
+
return isMetaModelConfiguration(config);
|
|
377
|
+
} catch {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Resolve a meta model configuration based on the first message or variant override.
|
|
384
|
+
* If the configuration is not a meta model, returns the configuration directly.
|
|
385
|
+
*
|
|
386
|
+
* @param configName The configuration name to resolve
|
|
387
|
+
* @param firstMessage Optional first user message for keyword detection
|
|
388
|
+
* @param variantOverride Optional variant name to use (bypasses keyword detection)
|
|
389
|
+
* @returns Resolution result with the actual configuration to use
|
|
390
|
+
*/
|
|
391
|
+
resolveMetaModel(configName?: string, firstMessage?: string, variantOverride?: string): MetaModelResolutionResult {
|
|
392
|
+
const rawConfig = this.getRawLLMConfig(configName);
|
|
393
|
+
|
|
394
|
+
// If not a meta model, return as-is
|
|
395
|
+
if (!isMetaModelConfiguration(rawConfig)) {
|
|
396
|
+
return {
|
|
397
|
+
config: rawConfig,
|
|
398
|
+
configName: configName || "default",
|
|
399
|
+
isMetaModel: false,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// It's a meta model - resolve based on override or keywords
|
|
404
|
+
const metaConfig = rawConfig as MetaModelConfiguration;
|
|
405
|
+
|
|
406
|
+
let resolution;
|
|
407
|
+
if (variantOverride) {
|
|
408
|
+
// Use the override variant directly (from change_model tool)
|
|
409
|
+
resolution = MetaModelResolver.resolveToVariant(metaConfig, variantOverride);
|
|
410
|
+
} else {
|
|
411
|
+
// Resolve based on keywords in the message
|
|
412
|
+
resolution = MetaModelResolver.resolve(metaConfig, firstMessage, {
|
|
413
|
+
stripKeywords: true,
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Get the resolved underlying configuration
|
|
418
|
+
const resolvedConfig = this.getLLMConfig(resolution.configName);
|
|
419
|
+
|
|
420
|
+
// Generate the system prompt fragment for model descriptions
|
|
421
|
+
const metaModelSystemPrompt = MetaModelResolver.generateSystemPromptFragment(metaConfig);
|
|
422
|
+
|
|
423
|
+
logger.info("[ConfigService] Resolved meta model", {
|
|
424
|
+
originalConfig: configName,
|
|
425
|
+
resolvedVariant: resolution.variantName,
|
|
426
|
+
resolvedConfig: resolution.configName,
|
|
427
|
+
matchedKeywords: resolution.matchedKeywords,
|
|
428
|
+
usedOverride: !!variantOverride,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
return {
|
|
432
|
+
config: resolvedConfig,
|
|
433
|
+
configName: resolution.configName,
|
|
434
|
+
strippedMessage: resolution.strippedMessage,
|
|
435
|
+
variantSystemPrompt: resolution.systemPrompt,
|
|
436
|
+
metaModelSystemPrompt,
|
|
437
|
+
isMetaModel: true,
|
|
438
|
+
variantName: resolution.variantName,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Create an LLM service for a named configuration
|
|
444
|
+
*/
|
|
445
|
+
createLLMService(
|
|
446
|
+
configName?: string,
|
|
447
|
+
context?: {
|
|
448
|
+
tools?: Record<string, unknown>;
|
|
449
|
+
agentName?: string;
|
|
450
|
+
/** Working directory path for Claude Code execution */
|
|
451
|
+
workingDirectory?: string;
|
|
452
|
+
sessionId?: string;
|
|
453
|
+
/** Agent-specific MCP configuration to merge with project/global config */
|
|
454
|
+
mcpConfig?: MCPConfig;
|
|
455
|
+
/** Conversation ID for OpenRouter correlation */
|
|
456
|
+
conversationId?: string;
|
|
457
|
+
/** Callback invoked when Claude Code stream starts, providing the message injector */
|
|
458
|
+
onStreamStart?: OnStreamStartCallback;
|
|
459
|
+
}
|
|
460
|
+
): LLMService {
|
|
461
|
+
const llmConfig = this.getLLMConfig(configName);
|
|
462
|
+
|
|
463
|
+
// Merge agent MCP config with project/global MCP config
|
|
464
|
+
// Agent config overrides project config for the same server names
|
|
465
|
+
let finalMcpConfig: MCPConfig | undefined;
|
|
466
|
+
|
|
467
|
+
if (this.loadedConfig?.mcp && context?.mcpConfig) {
|
|
468
|
+
// Merge: agent config overrides project config
|
|
469
|
+
finalMcpConfig = {
|
|
470
|
+
enabled: context.mcpConfig.enabled ?? this.loadedConfig.mcp.enabled,
|
|
471
|
+
servers: {
|
|
472
|
+
...this.loadedConfig.mcp.servers,
|
|
473
|
+
...context.mcpConfig.servers,
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
} else if (context?.mcpConfig) {
|
|
477
|
+
finalMcpConfig = context.mcpConfig;
|
|
478
|
+
} else if (this.loadedConfig?.mcp) {
|
|
479
|
+
finalMcpConfig = {
|
|
480
|
+
enabled: this.loadedConfig.mcp.enabled,
|
|
481
|
+
servers: this.loadedConfig.mcp.servers,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return llmServiceFactory.createService(llmConfig, {
|
|
486
|
+
...context,
|
|
487
|
+
mcpConfig: finalMcpConfig,
|
|
488
|
+
} as Parameters<typeof llmServiceFactory.createService>[1]);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// =====================================================================================
|
|
492
|
+
// BUSINESS LOGIC METHODS
|
|
493
|
+
// =====================================================================================
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Get the configured search model name if available.
|
|
497
|
+
* This is a typed accessor for the search configuration.
|
|
498
|
+
* @returns The search model config name or undefined if not configured
|
|
499
|
+
*/
|
|
500
|
+
getSearchModelName(): string | undefined {
|
|
501
|
+
return this.loadedConfig?.llms?.search;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Ensures that a backend private key exists for TENEX
|
|
506
|
+
* Generates a new one if not present
|
|
507
|
+
*/
|
|
508
|
+
async ensureBackendPrivateKey(): Promise<string> {
|
|
509
|
+
const globalPath = this.getGlobalPath();
|
|
510
|
+
const config = await this.loadTenexConfig(globalPath);
|
|
511
|
+
|
|
512
|
+
if (!config.tenexPrivateKey) {
|
|
513
|
+
// Generate new private key
|
|
514
|
+
const signer = NDKPrivateKeySigner.generate();
|
|
515
|
+
config.tenexPrivateKey = signer.privateKey;
|
|
516
|
+
|
|
517
|
+
// Save config with new key
|
|
518
|
+
await this.saveGlobalConfig(config);
|
|
519
|
+
logger.info("Generated new TENEX backend private key");
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return config.tenexPrivateKey;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Get the TENEX backend signer for publishing system-level events
|
|
527
|
+
*/
|
|
528
|
+
async getBackendSigner(): Promise<NDKPrivateKeySigner> {
|
|
529
|
+
const privateKey = await this.ensureBackendPrivateKey();
|
|
530
|
+
return new NDKPrivateKeySigner(privateKey);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Get whitelisted pubkeys with CLI override support
|
|
535
|
+
* If CLI option is provided, it ONLY uses those pubkeys (doesn't merge with config)
|
|
536
|
+
* Otherwise, returns pubkeys from the configuration
|
|
537
|
+
*/
|
|
538
|
+
getWhitelistedPubkeys(cliOption?: string, config?: TenexConfig): string[] {
|
|
539
|
+
const pubkeys: Set<string> = new Set();
|
|
540
|
+
|
|
541
|
+
// If CLI option is provided, ONLY use those pubkeys (don't merge with config)
|
|
542
|
+
if (cliOption) {
|
|
543
|
+
for (const pk of cliOption.split(",")) {
|
|
544
|
+
const trimmed = pk.trim();
|
|
545
|
+
if (trimmed) pubkeys.add(trimmed);
|
|
546
|
+
}
|
|
547
|
+
return Array.from(pubkeys);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Otherwise, use config pubkeys
|
|
551
|
+
if (config?.whitelistedPubkeys) {
|
|
552
|
+
if (Array.isArray(config.whitelistedPubkeys)) {
|
|
553
|
+
for (const pk of config.whitelistedPubkeys) {
|
|
554
|
+
if (pk) pubkeys.add(pk);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return Array.from(pubkeys);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// =====================================================================================
|
|
563
|
+
// CONVENIENCE METHODS
|
|
564
|
+
// =====================================================================================
|
|
565
|
+
|
|
566
|
+
async saveGlobalConfig(config: TenexConfig): Promise<void> {
|
|
567
|
+
const globalPath = this.getGlobalPath();
|
|
568
|
+
await ensureDirectory(globalPath);
|
|
569
|
+
await this.saveTenexConfig(globalPath, config);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async saveGlobalLLMs(llms: TenexLLMs): Promise<void> {
|
|
573
|
+
const globalPath = this.getGlobalPath();
|
|
574
|
+
await ensureDirectory(globalPath);
|
|
575
|
+
await this.saveTenexLLMs(globalPath, llms);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
async saveGlobalMCP(mcp: TenexMCP): Promise<void> {
|
|
579
|
+
const globalPath = this.getGlobalPath();
|
|
580
|
+
await ensureDirectory(globalPath);
|
|
581
|
+
await this.saveTenexMCP(globalPath, mcp);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async saveGlobalProviders(providers: TenexProviders): Promise<void> {
|
|
585
|
+
const globalPath = this.getGlobalPath();
|
|
586
|
+
await ensureDirectory(globalPath);
|
|
587
|
+
await this.saveTenexProviders(globalPath, providers);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// =====================================================================================
|
|
591
|
+
// FILE EXISTENCE CHECKS
|
|
592
|
+
// =====================================================================================
|
|
593
|
+
|
|
594
|
+
async configExists(basePath: string, configFile: ConfigFile): Promise<boolean> {
|
|
595
|
+
return fileExists(this.getConfigFilePath(basePath, configFile));
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
async globalConfigExists(configFile: ConfigFile): Promise<boolean> {
|
|
599
|
+
return this.configExists(this.getGlobalPath(), configFile);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// =====================================================================================
|
|
603
|
+
// PRIVATE IMPLEMENTATION
|
|
604
|
+
// =====================================================================================
|
|
605
|
+
|
|
606
|
+
private validateProviderReferences(llms: TenexLLMs, providers: TenexProviders): void {
|
|
607
|
+
const missingProviders = new Set<string>();
|
|
608
|
+
|
|
609
|
+
for (const configValue of Object.values(llms.configurations)) {
|
|
610
|
+
if (configValue.provider === "meta") continue;
|
|
611
|
+
|
|
612
|
+
const providerName = configValue.provider;
|
|
613
|
+
if (!providers.providers[providerName]) {
|
|
614
|
+
missingProviders.add(providerName);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (missingProviders.size > 0) {
|
|
619
|
+
logger.warn(
|
|
620
|
+
`LLM configurations reference providers not in providers.json: ${Array.from(missingProviders).join(", ")}`
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
private async loadConfigFile<T>(
|
|
626
|
+
filePath: string,
|
|
627
|
+
schema: z.ZodSchema<T>,
|
|
628
|
+
defaultValue: T
|
|
629
|
+
): Promise<T> {
|
|
630
|
+
// Check cache first
|
|
631
|
+
const cached = this.getFromCache<T>(filePath);
|
|
632
|
+
if (cached) {
|
|
633
|
+
return cached;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Check if file exists - if not, return default (this is expected)
|
|
637
|
+
if (!(await fileExists(filePath))) {
|
|
638
|
+
logger.debug(`Config file not found, using default: ${filePath}`);
|
|
639
|
+
return defaultValue;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// File exists - any error from here is a real problem that should propagate
|
|
643
|
+
try {
|
|
644
|
+
const data = await readJsonFile(filePath);
|
|
645
|
+
const validated = schema.parse(data);
|
|
646
|
+
|
|
647
|
+
this.addToCache(filePath, validated);
|
|
648
|
+
return validated;
|
|
649
|
+
} catch (error) {
|
|
650
|
+
// File exists but is corrupt/invalid - this is a real error, not a missing file
|
|
651
|
+
const errorMessage = formatAnyError(error);
|
|
652
|
+
logger.error(`Config file is corrupt or invalid: ${filePath}`, {
|
|
653
|
+
error: errorMessage,
|
|
654
|
+
});
|
|
655
|
+
throw new Error(
|
|
656
|
+
`Failed to load config file "${filePath}": ${errorMessage}. ` +
|
|
657
|
+
"Fix the file or delete it to use defaults.",
|
|
658
|
+
{ cause: error }
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
private async saveConfigFile<T>(
|
|
664
|
+
filePath: string,
|
|
665
|
+
data: T,
|
|
666
|
+
schema: z.ZodSchema<T>
|
|
667
|
+
): Promise<void> {
|
|
668
|
+
try {
|
|
669
|
+
// Ensure directory exists
|
|
670
|
+
await ensureDirectory(path.dirname(filePath));
|
|
671
|
+
|
|
672
|
+
// Validate before saving
|
|
673
|
+
const validated = schema.parse(data);
|
|
674
|
+
|
|
675
|
+
// Save to file
|
|
676
|
+
await writeJsonFile(filePath, validated);
|
|
677
|
+
|
|
678
|
+
// Update cache
|
|
679
|
+
this.addToCache(filePath, validated);
|
|
680
|
+
|
|
681
|
+
logger.debug(`Configuration saved: ${filePath}`);
|
|
682
|
+
} catch (error) {
|
|
683
|
+
logger.error(`Failed to save config file: ${filePath}`, {
|
|
684
|
+
error: formatAnyError(error),
|
|
685
|
+
});
|
|
686
|
+
throw error;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
private getFromCache<T>(filePath: string): T | null {
|
|
691
|
+
const entry = this.cache.get(filePath);
|
|
692
|
+
if (!entry) {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const now = Date.now();
|
|
697
|
+
if (now - entry.timestamp > 5000) {
|
|
698
|
+
// 5 seconds TTL
|
|
699
|
+
this.cache.delete(filePath);
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return entry.data as T;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
private addToCache<T>(filePath: string, data: T): void {
|
|
707
|
+
this.cache.set(filePath, {
|
|
708
|
+
data,
|
|
709
|
+
timestamp: Date.now(),
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
clearCache(filePath?: string): void {
|
|
714
|
+
if (filePath) {
|
|
715
|
+
this.cache.delete(filePath);
|
|
716
|
+
} else {
|
|
717
|
+
this.cache.clear();
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Export instance
|
|
723
|
+
export const config = new ConfigService();
|