@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,250 @@
|
|
|
1
|
+
import type { AISdkTool } from "@/tools/types";
|
|
2
|
+
import { logger } from "@/utils/logger";
|
|
3
|
+
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
4
|
+
import type {
|
|
5
|
+
StepResult,
|
|
6
|
+
StreamTextOnFinishCallback,
|
|
7
|
+
} from "ai";
|
|
8
|
+
import type { EventEmitter } from "tseep";
|
|
9
|
+
import { PROVIDER_IDS } from "./providers/provider-ids";
|
|
10
|
+
import { getInvalidToolCalls } from "./utils/tool-errors";
|
|
11
|
+
import { extractUsageMetadata, extractOpenRouterGenerationId } from "./providers/usage-metadata";
|
|
12
|
+
import type { LLMServiceEventMap } from "./types";
|
|
13
|
+
|
|
14
|
+
export interface FinishHandlerConfig {
|
|
15
|
+
provider: string;
|
|
16
|
+
model: string;
|
|
17
|
+
getModelContextWindow: () => number | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface FinishHandlerState {
|
|
21
|
+
getCachedContent: () => string;
|
|
22
|
+
clearCachedContent: () => void;
|
|
23
|
+
getLastUserMessage: () => string | undefined;
|
|
24
|
+
clearLastUserMessage: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Creates the onFinish handler for LLM streaming.
|
|
29
|
+
* Extracted from LLMService to reduce file size.
|
|
30
|
+
*/
|
|
31
|
+
export function createFinishHandler(
|
|
32
|
+
emitter: EventEmitter<LLMServiceEventMap>,
|
|
33
|
+
config: FinishHandlerConfig,
|
|
34
|
+
state: FinishHandlerState
|
|
35
|
+
): StreamTextOnFinishCallback<Record<string, AISdkTool>> {
|
|
36
|
+
return async (e) => {
|
|
37
|
+
const onFinishStartTime = Date.now();
|
|
38
|
+
const activeSpan = trace.getActiveSpan();
|
|
39
|
+
|
|
40
|
+
// DIAGNOSTIC: Track onFinish lifecycle for debugging race conditions
|
|
41
|
+
activeSpan?.addEvent("llm.onFinish_started", {
|
|
42
|
+
"onFinish.start_time": onFinishStartTime,
|
|
43
|
+
"onFinish.finish_reason": e.finishReason,
|
|
44
|
+
"onFinish.steps_count": e.steps.length,
|
|
45
|
+
"onFinish.text_length": e.text?.length ?? 0,
|
|
46
|
+
"onFinish.cached_content_length": state.getCachedContent().length,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
recordInvalidToolCalls(e.steps, "response", config.model, config.provider);
|
|
51
|
+
|
|
52
|
+
// For streaming, use cached content if available.
|
|
53
|
+
// When cachedContentForComplete is empty but e.text has content, it means:
|
|
54
|
+
// - Content was already published as conversation event(s) via chunk-type-change
|
|
55
|
+
// - BUT we still need to re-publish it in the completion event for delegations
|
|
56
|
+
// This creates intentional duplication (conversation + completion both have same text)
|
|
57
|
+
// but ensures delegated agents receive the full response via p-tag on completion.
|
|
58
|
+
//
|
|
59
|
+
// Three-level fallback for finalMessage:
|
|
60
|
+
// 1. Use cachedContent if available (normal case)
|
|
61
|
+
// 2. Use e.text if cachedContent is empty AND e.text is non-empty (fallback when content was already published)
|
|
62
|
+
// 3. Use error message if both are empty (edge case where no content was captured)
|
|
63
|
+
const ERROR_FALLBACK_MESSAGE =
|
|
64
|
+
"There was an error capturing the work done, please review the conversation for the results";
|
|
65
|
+
|
|
66
|
+
const cachedContent = state.getCachedContent();
|
|
67
|
+
const text = e.text ?? "";
|
|
68
|
+
|
|
69
|
+
const fallbackLevel =
|
|
70
|
+
cachedContent.length > 0 ? "cached" :
|
|
71
|
+
text.length > 0 ? "text" :
|
|
72
|
+
"error";
|
|
73
|
+
|
|
74
|
+
const finalMessage =
|
|
75
|
+
fallbackLevel === "cached" ? cachedContent :
|
|
76
|
+
fallbackLevel === "text" ? text :
|
|
77
|
+
ERROR_FALLBACK_MESSAGE;
|
|
78
|
+
|
|
79
|
+
const usedFallbackToText = fallbackLevel === "text";
|
|
80
|
+
const usedErrorFallback = fallbackLevel === "error";
|
|
81
|
+
|
|
82
|
+
emitSessionCapturedFromMetadata(
|
|
83
|
+
emitter,
|
|
84
|
+
config.provider,
|
|
85
|
+
e.providerMetadata as Record<string, unknown> | undefined,
|
|
86
|
+
false
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Capture OpenRouter generation ID for trace correlation
|
|
90
|
+
const openrouterGenerationId = extractOpenRouterGenerationId(
|
|
91
|
+
e.providerMetadata as Record<string, unknown> | undefined
|
|
92
|
+
);
|
|
93
|
+
if (openrouterGenerationId) {
|
|
94
|
+
activeSpan?.setAttribute("openrouter.generation_id", openrouterGenerationId);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Extract usage metadata using provider-specific extractor
|
|
98
|
+
const usage = extractUsageMetadata(
|
|
99
|
+
config.provider,
|
|
100
|
+
config.model,
|
|
101
|
+
e.totalUsage,
|
|
102
|
+
e.providerMetadata as Record<string, unknown> | undefined
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// DIAGNOSTIC: Log right before emitting complete event
|
|
106
|
+
const beforeEmitTime = Date.now();
|
|
107
|
+
activeSpan?.addEvent("llm.complete_will_emit", {
|
|
108
|
+
"complete.message_length": finalMessage.length,
|
|
109
|
+
"complete.cached_content_length": cachedContent.length,
|
|
110
|
+
"complete.e_text_length": text.length,
|
|
111
|
+
"complete.used_fallback_to_e_text": usedFallbackToText,
|
|
112
|
+
"complete.used_error_fallback": usedErrorFallback,
|
|
113
|
+
"complete.usage_input_tokens": usage.inputTokens,
|
|
114
|
+
"complete.usage_output_tokens": usage.outputTokens,
|
|
115
|
+
"complete.finish_reason": e.finishReason,
|
|
116
|
+
"complete.ms_since_onFinish_start": beforeEmitTime - onFinishStartTime,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
emitter.emit("complete", {
|
|
120
|
+
message: finalMessage,
|
|
121
|
+
steps: e.steps,
|
|
122
|
+
usage: {
|
|
123
|
+
...usage,
|
|
124
|
+
contextWindow: config.getModelContextWindow(),
|
|
125
|
+
},
|
|
126
|
+
finishReason: e.finishReason,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// DIAGNOSTIC: Log after emitting complete event
|
|
130
|
+
const afterEmitTime = Date.now();
|
|
131
|
+
activeSpan?.addEvent("llm.complete_did_emit", {
|
|
132
|
+
"complete.emit_duration_ms": afterEmitTime - beforeEmitTime,
|
|
133
|
+
"complete.total_onFinish_duration_ms": afterEmitTime - onFinishStartTime,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Log the user prompt so we can see what the LLM was answering to
|
|
137
|
+
const lastUserMessage = state.getLastUserMessage();
|
|
138
|
+
if (lastUserMessage) {
|
|
139
|
+
const truncatedPrompt = lastUserMessage.length > 2000
|
|
140
|
+
? lastUserMessage.substring(0, 2000) + "... [truncated]"
|
|
141
|
+
: lastUserMessage;
|
|
142
|
+
activeSpan?.addEvent("llm.prompt", {
|
|
143
|
+
"prompt.text": truncatedPrompt,
|
|
144
|
+
"prompt.full_length": lastUserMessage.length,
|
|
145
|
+
"prompt.truncated": lastUserMessage.length > 2000,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Log the actual response text so it shows up in Jaeger's Logs section
|
|
150
|
+
// This makes it much easier to see what the LLM actually generated
|
|
151
|
+
if (e.text) {
|
|
152
|
+
// Truncate to avoid massive log entries (OTel has limits)
|
|
153
|
+
const truncatedText = e.text.length > 4000
|
|
154
|
+
? e.text.substring(0, 4000) + "... [truncated]"
|
|
155
|
+
: e.text;
|
|
156
|
+
activeSpan?.addEvent("llm.response", {
|
|
157
|
+
"response.text": truncatedText,
|
|
158
|
+
"response.full_length": e.text.length,
|
|
159
|
+
"response.truncated": e.text.length > 4000,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Clear cached content after use
|
|
164
|
+
state.clearCachedContent();
|
|
165
|
+
state.clearLastUserMessage();
|
|
166
|
+
} catch (error) {
|
|
167
|
+
const errorTime = Date.now();
|
|
168
|
+
activeSpan?.addEvent("llm.onFinish_error", {
|
|
169
|
+
"error.message": error instanceof Error ? error.message : String(error),
|
|
170
|
+
"error.type": error instanceof Error ? error.constructor.name : typeof error,
|
|
171
|
+
"error.ms_since_onFinish_start": errorTime - onFinishStartTime,
|
|
172
|
+
});
|
|
173
|
+
logger.error("[LLMService] Error in onFinish handler", {
|
|
174
|
+
error: error instanceof Error ? error.message : String(error),
|
|
175
|
+
});
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Record invalid tool calls to the active span
|
|
183
|
+
*/
|
|
184
|
+
function recordInvalidToolCalls(
|
|
185
|
+
steps: StepResult<Record<string, AISdkTool>>[],
|
|
186
|
+
logContext: "complete" | "response",
|
|
187
|
+
model: string,
|
|
188
|
+
provider: string
|
|
189
|
+
): void {
|
|
190
|
+
const activeSpan = trace.getActiveSpan();
|
|
191
|
+
if (!activeSpan) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const invalidToolCalls = getInvalidToolCalls(steps);
|
|
196
|
+
if (invalidToolCalls.length === 0) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const logSuffix = logContext === "complete" ? "complete()" : "response";
|
|
201
|
+
|
|
202
|
+
activeSpan.setStatus({
|
|
203
|
+
code: SpanStatusCode.ERROR,
|
|
204
|
+
message: `Invalid tool calls: ${invalidToolCalls.map((tc) => tc.toolName).join(", ")}`,
|
|
205
|
+
});
|
|
206
|
+
activeSpan.setAttribute("error", true);
|
|
207
|
+
activeSpan.setAttribute("error.type", "AI_InvalidToolCall");
|
|
208
|
+
activeSpan.setAttribute("error.invalid_tool_count", invalidToolCalls.length);
|
|
209
|
+
activeSpan.setAttribute(
|
|
210
|
+
"error.invalid_tools",
|
|
211
|
+
invalidToolCalls.map((tc) => tc.toolName).join(", ")
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
for (const invalidTool of invalidToolCalls) {
|
|
215
|
+
activeSpan.addEvent("invalid_tool_call", {
|
|
216
|
+
"tool.name": invalidTool.toolName,
|
|
217
|
+
"error.type": invalidTool.error,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
logger.error(`[LLMService] Invalid tool calls detected in ${logSuffix}`, {
|
|
222
|
+
invalidToolCalls,
|
|
223
|
+
model,
|
|
224
|
+
provider,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Emit session-captured event from provider metadata
|
|
230
|
+
*/
|
|
231
|
+
function emitSessionCapturedFromMetadata(
|
|
232
|
+
emitter: EventEmitter<LLMServiceEventMap>,
|
|
233
|
+
provider: string,
|
|
234
|
+
providerMetadata: Record<string, unknown> | undefined,
|
|
235
|
+
recordSpanEvent: boolean
|
|
236
|
+
): void {
|
|
237
|
+
if (provider === PROVIDER_IDS.CODEX_APP_SERVER) {
|
|
238
|
+
const sessionId = (
|
|
239
|
+
providerMetadata?.[PROVIDER_IDS.CODEX_APP_SERVER] as { sessionId?: string } | undefined
|
|
240
|
+
)?.sessionId;
|
|
241
|
+
if (sessionId) {
|
|
242
|
+
if (recordSpanEvent) {
|
|
243
|
+
trace.getActiveSpan()?.addEvent("llm.session_captured", {
|
|
244
|
+
"session.id": sessionId,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
emitter.emit("session-captured", { sessionId });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { config } from "@/services/ConfigService";
|
|
2
|
+
import type { TenexLLMs } from "@/services/config/types";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import inquirer from "inquirer";
|
|
5
|
+
import { llmServiceFactory } from "./LLMServiceFactory";
|
|
6
|
+
import { ConfigurationManager } from "./utils/ConfigurationManager";
|
|
7
|
+
import { ConfigurationTester } from "./utils/ConfigurationTester";
|
|
8
|
+
import { ProviderConfigUI } from "./utils/ProviderConfigUI";
|
|
9
|
+
import { runProviderSetup } from "./utils/provider-setup";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal type used by editor to work with providers
|
|
13
|
+
* Merges providers with llms for internal convenience
|
|
14
|
+
*/
|
|
15
|
+
type LLMConfigWithProviders = TenexLLMs & {
|
|
16
|
+
providers: Record<string, { apiKey: string | string[] }>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* LLM Configuration Editor - Simple menu orchestrator
|
|
21
|
+
* Note: LLM configs are now global only (no project-level llms.json)
|
|
22
|
+
*/
|
|
23
|
+
export class LLMConfigEditor {
|
|
24
|
+
constructor() {}
|
|
25
|
+
|
|
26
|
+
async showMainMenu(): Promise<void> {
|
|
27
|
+
const llmsConfig = await this.loadConfig();
|
|
28
|
+
|
|
29
|
+
console.log(chalk.cyan("\n=== LLM Configuration ===\n"));
|
|
30
|
+
ProviderConfigUI.displayCurrentConfig(llmsConfig);
|
|
31
|
+
|
|
32
|
+
const { action } = await inquirer.prompt([
|
|
33
|
+
{
|
|
34
|
+
type: "select",
|
|
35
|
+
name: "action",
|
|
36
|
+
message: "What would you like to do?",
|
|
37
|
+
choices: [
|
|
38
|
+
{ name: "Add new configuration", value: "add" },
|
|
39
|
+
{ name: "Create meta model", value: "addMeta" },
|
|
40
|
+
{ name: "Delete configuration", value: "delete" },
|
|
41
|
+
{
|
|
42
|
+
name: `Default agents' model: ${llmsConfig.default || "none"}`,
|
|
43
|
+
value: "default",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: `Summarization model: ${llmsConfig.summarization || "none"}`,
|
|
47
|
+
value: "summarization",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: `Supervision model: ${llmsConfig.supervision || "none"}`,
|
|
51
|
+
value: "supervision",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: `Search model: ${llmsConfig.search || "none"}`,
|
|
55
|
+
value: "search",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: `Prompt compilation model: ${llmsConfig.promptCompilation || "none"}`,
|
|
59
|
+
value: "promptCompilation",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: `Compression model: ${llmsConfig.compression || "none"}`,
|
|
63
|
+
value: "compression",
|
|
64
|
+
},
|
|
65
|
+
{ name: "Test configuration", value: "test" },
|
|
66
|
+
{ name: "Exit", value: "exit" },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
if (action === "exit") process.exit(0);
|
|
72
|
+
|
|
73
|
+
if (action === "test") {
|
|
74
|
+
await ConfigurationTester.test(llmsConfig);
|
|
75
|
+
} else {
|
|
76
|
+
// All other actions use ConfigurationManager
|
|
77
|
+
if (action === "add") await ConfigurationManager.add(llmsConfig);
|
|
78
|
+
if (action === "addMeta") await ConfigurationManager.addMetaModel(llmsConfig);
|
|
79
|
+
if (action === "delete") await ConfigurationManager.delete(llmsConfig);
|
|
80
|
+
if (action === "default") await ConfigurationManager.setDefault(llmsConfig);
|
|
81
|
+
if (action === "summarization") await ConfigurationManager.setSummarizationModel(llmsConfig);
|
|
82
|
+
if (action === "supervision") await ConfigurationManager.setSupervisionModel(llmsConfig);
|
|
83
|
+
if (action === "search") await ConfigurationManager.setSearchModel(llmsConfig);
|
|
84
|
+
if (action === "promptCompilation") await ConfigurationManager.setPromptCompilationModel(llmsConfig);
|
|
85
|
+
if (action === "compression") await ConfigurationManager.setCompressionModel(llmsConfig);
|
|
86
|
+
await this.saveConfig(llmsConfig);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await this.showMainMenu();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async runOnboardingFlow(): Promise<void> {
|
|
93
|
+
console.log(chalk.green("\n🚀 Welcome to TENEX LLM Setup!\n"));
|
|
94
|
+
|
|
95
|
+
const llmsConfig = await this.loadConfig();
|
|
96
|
+
const globalPath = config.getGlobalPath();
|
|
97
|
+
|
|
98
|
+
// Step 1: Configure providers
|
|
99
|
+
console.log(chalk.cyan("Step 1: Configure Provider API Keys"));
|
|
100
|
+
const existingProviders = await config.loadTenexProviders(globalPath);
|
|
101
|
+
const updatedProviders = await runProviderSetup(existingProviders);
|
|
102
|
+
llmsConfig.providers = updatedProviders.providers;
|
|
103
|
+
await this.saveConfig(llmsConfig);
|
|
104
|
+
|
|
105
|
+
// Step 2: Create first configuration
|
|
106
|
+
console.log(chalk.cyan("\nStep 2: Create Your First Configuration"));
|
|
107
|
+
await ConfigurationManager.add(llmsConfig, true);
|
|
108
|
+
await this.saveConfig(llmsConfig);
|
|
109
|
+
|
|
110
|
+
// Step 3: Offer to test
|
|
111
|
+
const { shouldTest } = await inquirer.prompt([
|
|
112
|
+
{
|
|
113
|
+
type: "confirm",
|
|
114
|
+
name: "shouldTest",
|
|
115
|
+
message: "Would you like to test your configuration?",
|
|
116
|
+
default: true,
|
|
117
|
+
},
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
if (shouldTest) {
|
|
121
|
+
await ConfigurationTester.test(llmsConfig);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log(chalk.green("\n✅ LLM configuration complete!"));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private async loadConfig(): Promise<LLMConfigWithProviders> {
|
|
128
|
+
const globalPath = config.getGlobalPath();
|
|
129
|
+
|
|
130
|
+
// Load providers and llms separately
|
|
131
|
+
const providersConfig = await config.loadTenexProviders(globalPath);
|
|
132
|
+
const llmsConfig = await config.loadTenexLLMs(globalPath);
|
|
133
|
+
|
|
134
|
+
// Merge for internal editor use
|
|
135
|
+
return {
|
|
136
|
+
...llmsConfig,
|
|
137
|
+
providers: providersConfig.providers,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private async saveConfig(llmsConfig: LLMConfigWithProviders): Promise<void> {
|
|
142
|
+
// Split providers and llms for separate storage
|
|
143
|
+
const { providers, ...llmsWithoutProviders } = llmsConfig;
|
|
144
|
+
|
|
145
|
+
// Save providers to providers.json
|
|
146
|
+
await config.saveGlobalProviders({ providers });
|
|
147
|
+
|
|
148
|
+
// Save llms to llms.json
|
|
149
|
+
await config.saveGlobalLLMs(llmsWithoutProviders as TenexLLMs);
|
|
150
|
+
|
|
151
|
+
// Re-initialize factory with updated providers
|
|
152
|
+
await llmServiceFactory.initializeProviders(providers);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Service Factory
|
|
3
|
+
*
|
|
4
|
+
* Factory for creating LLM services with proper provider initialization.
|
|
5
|
+
* This module provides a simplified interface that delegates to the
|
|
6
|
+
* modular ProviderRegistry for actual provider management.
|
|
7
|
+
*
|
|
8
|
+
* @see src/llm/providers for individual provider implementations
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { LLMConfiguration } from "@/services/config/types";
|
|
12
|
+
import type { AISdkTool } from "@/tools/types";
|
|
13
|
+
import { logger } from "@/utils/logger";
|
|
14
|
+
import type { LanguageModel, ProviderRegistryProvider } from "ai";
|
|
15
|
+
import type { ClaudeCodeSettings } from "ai-sdk-provider-claude-code";
|
|
16
|
+
|
|
17
|
+
import { LLMService } from "./service";
|
|
18
|
+
import {
|
|
19
|
+
providerRegistry,
|
|
20
|
+
type MCPConfig,
|
|
21
|
+
type ProviderPoolConfig,
|
|
22
|
+
type ProviderRuntimeContext,
|
|
23
|
+
} from "./providers";
|
|
24
|
+
import { PROVIDER_IDS } from "./providers/provider-ids";
|
|
25
|
+
import type { OnStreamStartCallback } from "./types";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Factory for creating LLM services with proper provider initialization
|
|
29
|
+
*
|
|
30
|
+
* This factory provides a high-level interface for:
|
|
31
|
+
* - Initializing providers from configuration
|
|
32
|
+
* - Creating LLMService instances
|
|
33
|
+
* - Checking provider availability
|
|
34
|
+
*
|
|
35
|
+
* The actual provider management is delegated to the ProviderRegistry.
|
|
36
|
+
*/
|
|
37
|
+
export class LLMServiceFactory {
|
|
38
|
+
private initialized = false;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Initialize providers from configuration
|
|
42
|
+
*
|
|
43
|
+
* @param providerConfigs Map of provider names to their configurations
|
|
44
|
+
* @param options Additional options for initialization
|
|
45
|
+
*/
|
|
46
|
+
async initializeProviders(
|
|
47
|
+
providerConfigs: Record<string, { apiKey: string | string[] }>
|
|
48
|
+
): Promise<void> {
|
|
49
|
+
// Convert to ProviderPoolConfig format
|
|
50
|
+
// apiKey can be a single string or an array — KeyManager handles the rest
|
|
51
|
+
const configs: Record<string, ProviderPoolConfig> = {};
|
|
52
|
+
for (const [name, config] of Object.entries(providerConfigs)) {
|
|
53
|
+
const hasKey = Array.isArray(config?.apiKey)
|
|
54
|
+
? config.apiKey.length > 0
|
|
55
|
+
: !!config?.apiKey;
|
|
56
|
+
|
|
57
|
+
if (hasKey) {
|
|
58
|
+
configs[name] = {
|
|
59
|
+
apiKey: config.apiKey,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Also ensure agent providers are initialized (they don't need API keys).
|
|
65
|
+
// Add them with empty configs if not already present.
|
|
66
|
+
const agentProviders = [PROVIDER_IDS.CLAUDE_CODE, PROVIDER_IDS.CODEX_APP_SERVER];
|
|
67
|
+
for (const providerId of agentProviders) {
|
|
68
|
+
if (!configs[providerId]) {
|
|
69
|
+
configs[providerId] = {};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Initialize through the registry (which has its own tracing span)
|
|
74
|
+
const results = await providerRegistry.initialize(configs);
|
|
75
|
+
|
|
76
|
+
// Log initialization failures
|
|
77
|
+
const failed = results.filter(r => !r.success);
|
|
78
|
+
if (failed.length > 0) {
|
|
79
|
+
for (const f of failed) {
|
|
80
|
+
logger.error(`[LLMServiceFactory] Failed to initialize provider ${f.providerId}`, {
|
|
81
|
+
error: f.error,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.initialized = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create an LLM service from a resolved configuration
|
|
91
|
+
*
|
|
92
|
+
* @param config LLM configuration
|
|
93
|
+
* @param context Optional runtime context for agents
|
|
94
|
+
*/
|
|
95
|
+
createService(
|
|
96
|
+
config: LLMConfiguration,
|
|
97
|
+
context?: {
|
|
98
|
+
tools?: Record<string, AISdkTool>;
|
|
99
|
+
agentName?: string;
|
|
100
|
+
sessionId?: string;
|
|
101
|
+
/** Working directory path for agent execution */
|
|
102
|
+
workingDirectory?: string;
|
|
103
|
+
/** MCP configuration - passed from services layer to providers */
|
|
104
|
+
mcpConfig?: MCPConfig;
|
|
105
|
+
/** Conversation ID for OpenRouter correlation */
|
|
106
|
+
conversationId?: string;
|
|
107
|
+
/** Callback invoked when Claude Code stream starts, providing the message injector */
|
|
108
|
+
onStreamStart?: OnStreamStartCallback;
|
|
109
|
+
}
|
|
110
|
+
): LLMService {
|
|
111
|
+
if (!this.initialized) {
|
|
112
|
+
throw new Error("LLMServiceFactory not initialized. Call initializeProviders first.");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Convert agent name to slug format for telemetry
|
|
116
|
+
const agentSlug = context?.agentName
|
|
117
|
+
? context.agentName.toLowerCase().replace(/\s+/g, "-")
|
|
118
|
+
: undefined;
|
|
119
|
+
|
|
120
|
+
// Determine the actual provider (mock mode handling)
|
|
121
|
+
const actualProvider = process.env.USE_MOCK_LLM === "true" ? "mock" : config.provider;
|
|
122
|
+
|
|
123
|
+
// Build the runtime context for the provider
|
|
124
|
+
const runtimeContext: ProviderRuntimeContext = {
|
|
125
|
+
tools: context?.tools,
|
|
126
|
+
agentName: context?.agentName,
|
|
127
|
+
sessionId: context?.sessionId,
|
|
128
|
+
workingDirectory: context?.workingDirectory,
|
|
129
|
+
mcpConfig: context?.mcpConfig,
|
|
130
|
+
reasoningEffort: (config as { reasoningEffort?: "none" | "low" | "medium" | "high" | "xhigh" }).reasoningEffort,
|
|
131
|
+
onStreamStart: context?.onStreamStart,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Get the provider from the registry
|
|
135
|
+
const provider = providerRegistry.getProvider(actualProvider);
|
|
136
|
+
|
|
137
|
+
if (!provider) {
|
|
138
|
+
const available = providerRegistry.getAvailableProviders().map(p => p.id);
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Provider "${actualProvider}" not available. ` +
|
|
141
|
+
`Initialized providers: ${available.length > 0 ? available.join(", ") : "none"}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Create the model from the provider
|
|
146
|
+
const modelResult = provider.createModel(config.model, runtimeContext);
|
|
147
|
+
|
|
148
|
+
// Get capabilities from provider metadata
|
|
149
|
+
const capabilities = provider.metadata.capabilities;
|
|
150
|
+
|
|
151
|
+
// For agent providers (claude-code, codex-app-server), use their provider function
|
|
152
|
+
if (modelResult.bypassRegistry && modelResult.providerFunction) {
|
|
153
|
+
return new LLMService(
|
|
154
|
+
null,
|
|
155
|
+
actualProvider,
|
|
156
|
+
config.model,
|
|
157
|
+
capabilities,
|
|
158
|
+
config.temperature,
|
|
159
|
+
config.maxTokens,
|
|
160
|
+
modelResult.providerFunction as (model: string, options?: ClaudeCodeSettings) => LanguageModel,
|
|
161
|
+
modelResult.agentSettings as ClaudeCodeSettings,
|
|
162
|
+
context?.sessionId,
|
|
163
|
+
agentSlug,
|
|
164
|
+
context?.conversationId
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// For standard providers, use the AI SDK registry
|
|
169
|
+
const registry = providerRegistry.getAiSdkRegistry();
|
|
170
|
+
|
|
171
|
+
return new LLMService(
|
|
172
|
+
registry,
|
|
173
|
+
actualProvider,
|
|
174
|
+
config.model,
|
|
175
|
+
capabilities,
|
|
176
|
+
config.temperature,
|
|
177
|
+
config.maxTokens,
|
|
178
|
+
undefined,
|
|
179
|
+
undefined,
|
|
180
|
+
context?.sessionId,
|
|
181
|
+
agentSlug,
|
|
182
|
+
context?.conversationId
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Check if a provider is available
|
|
188
|
+
*/
|
|
189
|
+
hasProvider(providerName: string): boolean {
|
|
190
|
+
return providerRegistry.hasProvider(providerName);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Get the AI SDK provider registry
|
|
195
|
+
* Useful for direct access to language models
|
|
196
|
+
*/
|
|
197
|
+
getRegistry(): ProviderRegistryProvider {
|
|
198
|
+
if (!this.initialized) {
|
|
199
|
+
throw new Error("LLMServiceFactory not initialized. Call initializeProviders first.");
|
|
200
|
+
}
|
|
201
|
+
return providerRegistry.getAiSdkRegistry();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get list of available providers
|
|
206
|
+
*/
|
|
207
|
+
getAvailableProviders(): string[] {
|
|
208
|
+
return providerRegistry.getAvailableProviders().map(p => p.id);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get list of all registered providers (even if not initialized)
|
|
213
|
+
*/
|
|
214
|
+
getRegisteredProviders(): string[] {
|
|
215
|
+
return providerRegistry.getRegisteredProviders().map(p => p.id);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Reset the factory (mainly for testing)
|
|
220
|
+
*/
|
|
221
|
+
reset(): void {
|
|
222
|
+
providerRegistry.reset();
|
|
223
|
+
this.initialized = false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Export singleton instance
|
|
229
|
+
*/
|
|
230
|
+
export const llmServiceFactory = new LLMServiceFactory();
|