@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,657 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import { config } from "@/services/ConfigService";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { AgentRegistry } from "@/agents/AgentRegistry";
|
|
5
|
+
import { checkSupervisionHealth, registerDefaultHeuristics } from "@/agents/supervision";
|
|
6
|
+
import { ConversationStore } from "@/conversations/ConversationStore";
|
|
7
|
+
import { EventHandler } from "@/event-handler";
|
|
8
|
+
import { NDKMCPTool } from "@/events/NDKMCPTool";
|
|
9
|
+
import { getNDK } from "@/nostr";
|
|
10
|
+
import { ProjectContext } from "@/services/projects";
|
|
11
|
+
import { projectContextStore } from "@/services/projects";
|
|
12
|
+
import { MCPManager } from "@/services/mcp/MCPManager";
|
|
13
|
+
import { McpSubscriptionService } from "@/services/mcp/McpSubscriptionService";
|
|
14
|
+
import { deliverMcpNotification } from "@/services/mcp/McpNotificationDelivery";
|
|
15
|
+
import { installMCPServerFromEvent } from "@/services/mcp/mcpInstaller";
|
|
16
|
+
import { createLocalReportStore, LocalReportStore } from "@/services/reports";
|
|
17
|
+
import { ProjectStatusService } from "@/services/status/ProjectStatusService";
|
|
18
|
+
import { OperationsStatusService } from "@/services/status/OperationsStatusService";
|
|
19
|
+
import { prefixKVStore } from "@/services/storage";
|
|
20
|
+
import { RALRegistry } from "@/services/ral";
|
|
21
|
+
import { getPubkeyService } from "@/services/PubkeyService";
|
|
22
|
+
import { getTrustPubkeyService } from "@/services/trust-pubkeys";
|
|
23
|
+
import { cloneGitRepository, initializeGitRepository } from "@/utils/git";
|
|
24
|
+
import { logger } from "@/utils/logger";
|
|
25
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
26
|
+
import type { NDKProject } from "@nostr-dev-kit/ndk";
|
|
27
|
+
import { trace, SpanStatusCode } from "@opentelemetry/api";
|
|
28
|
+
import chalk from "chalk";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Self-contained runtime for a single project.
|
|
32
|
+
* Manages its own lifecycle, status publishing, and event handling.
|
|
33
|
+
*/
|
|
34
|
+
export class ProjectRuntime {
|
|
35
|
+
public readonly projectId: string;
|
|
36
|
+
/**
|
|
37
|
+
* Project directory (normal git repository root).
|
|
38
|
+
* Example: ~/tenex/{dTag}
|
|
39
|
+
* The default branch is checked out here directly.
|
|
40
|
+
* Worktrees go in .worktrees/ subdirectory.
|
|
41
|
+
*/
|
|
42
|
+
public readonly projectBasePath: string;
|
|
43
|
+
private readonly metadataPath: string; // TENEX metadata path
|
|
44
|
+
private readonly dTag: string;
|
|
45
|
+
|
|
46
|
+
private project: NDKProject;
|
|
47
|
+
private context: ProjectContext | null = null;
|
|
48
|
+
private eventHandler: EventHandler | null = null;
|
|
49
|
+
private statusPublisher: ProjectStatusService | null = null;
|
|
50
|
+
private operationsStatusPublisher: OperationsStatusService | null = null;
|
|
51
|
+
private mcpManager: MCPManager = new MCPManager();
|
|
52
|
+
private localReportStore: LocalReportStore = createLocalReportStore();
|
|
53
|
+
|
|
54
|
+
private isRunning = false;
|
|
55
|
+
private startTime: Date | null = null;
|
|
56
|
+
private lastEventTime: Date | null = null;
|
|
57
|
+
private eventCount = 0;
|
|
58
|
+
private prefixStoreInitialized = false;
|
|
59
|
+
|
|
60
|
+
constructor(project: NDKProject, projectsBase: string) {
|
|
61
|
+
this.project = project;
|
|
62
|
+
|
|
63
|
+
// Build project ID: "31933:authorPubkey:dTag"
|
|
64
|
+
const dTag = project.tagValue("d");
|
|
65
|
+
if (!dTag) {
|
|
66
|
+
throw new Error("Project missing required d tag");
|
|
67
|
+
}
|
|
68
|
+
this.dTag = dTag;
|
|
69
|
+
this.projectId = `31933:${project.pubkey}:${dTag}`;
|
|
70
|
+
|
|
71
|
+
// Project directory: {projectsBase}/{dTag}
|
|
72
|
+
// Normal git repo with default branch checked out.
|
|
73
|
+
// Worktrees go in .worktrees/ subdirectory.
|
|
74
|
+
this.projectBasePath = path.join(projectsBase, dTag);
|
|
75
|
+
|
|
76
|
+
// TENEX metadata (hidden): ~/.tenex/projects/{dTag}
|
|
77
|
+
this.metadataPath = path.join(config.getConfigPath("projects"), dTag);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Start the project runtime
|
|
82
|
+
*/
|
|
83
|
+
async start(): Promise<void> {
|
|
84
|
+
if (this.isRunning) {
|
|
85
|
+
logger.warn(`Project runtime already running: ${this.projectId}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const projectTitle = this.project.tagValue("title") || "Untitled";
|
|
90
|
+
console.log(chalk.yellow(`🚀 Starting project: ${chalk.bold(projectTitle)}`));
|
|
91
|
+
|
|
92
|
+
trace.getActiveSpan()?.addEvent("project_runtime.starting", {
|
|
93
|
+
"project.id": this.projectId,
|
|
94
|
+
"project.title": this.project.tagValue("title") ?? "",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// Create TENEX metadata directories: ~/.tenex/projects/<dTag>/{conversations,logs}
|
|
99
|
+
await fs.mkdir(path.join(this.metadataPath, "conversations"), { recursive: true });
|
|
100
|
+
await fs.mkdir(path.join(this.metadataPath, "logs"), { recursive: true });
|
|
101
|
+
|
|
102
|
+
// Clone or init git repository at ~/tenex/<dTag>/
|
|
103
|
+
const repoUrl = this.project.repo;
|
|
104
|
+
|
|
105
|
+
if (repoUrl) {
|
|
106
|
+
trace.getActiveSpan()?.addEvent("project_runtime.cloning_repo", {
|
|
107
|
+
"repo.url": repoUrl,
|
|
108
|
+
});
|
|
109
|
+
const result = await cloneGitRepository(repoUrl, this.projectBasePath);
|
|
110
|
+
if (!result) {
|
|
111
|
+
throw new Error(`Failed to clone repository: ${repoUrl}`);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
trace.getActiveSpan()?.addEvent("project_runtime.init_repo");
|
|
115
|
+
await initializeGitRepository(this.projectBasePath);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
trace.getActiveSpan()?.addEvent("project_runtime.repo_ready", {
|
|
119
|
+
"project.path": this.projectBasePath,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Initialize components
|
|
123
|
+
const agentRegistry = new AgentRegistry(this.projectBasePath, this.metadataPath);
|
|
124
|
+
await agentRegistry.loadFromProject(this.project);
|
|
125
|
+
|
|
126
|
+
// Verify supervision system health (fail-fast if misconfigured)
|
|
127
|
+
await this.verifySupervisionHealth();
|
|
128
|
+
|
|
129
|
+
// Create project context directly (don't use global singleton)
|
|
130
|
+
this.context = new ProjectContext(this.project, agentRegistry);
|
|
131
|
+
|
|
132
|
+
// Initialize prefix KV store and index agent pubkeys
|
|
133
|
+
// This is best-effort - indexing failures don't block project startup
|
|
134
|
+
await prefixKVStore.initialize();
|
|
135
|
+
this.prefixStoreInitialized = true;
|
|
136
|
+
const agentPubkeysForIndex = Array.from(this.context.agents.values()).map(a => a.pubkey);
|
|
137
|
+
try {
|
|
138
|
+
await prefixKVStore.addBatch(agentPubkeysForIndex);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
logger.warn("[ProjectRuntime] Failed to index agent pubkeys for prefix lookup", {
|
|
141
|
+
projectId: this.projectId,
|
|
142
|
+
error: error instanceof Error ? error.message : String(error),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Load MCP tools from project event
|
|
147
|
+
await this.initializeMCPTools();
|
|
148
|
+
|
|
149
|
+
// Set mcpManager on context for use by tools and services
|
|
150
|
+
this.context.mcpManager = this.mcpManager;
|
|
151
|
+
|
|
152
|
+
// Initialize MCP subscription service for resource notification subscriptions
|
|
153
|
+
// Must be done after mcpManager is set on context (subscriptions need MCP access).
|
|
154
|
+
// The notification handler is wrapped in projectContextStore.run() because MCP
|
|
155
|
+
// push notifications fire from SDK transport callbacks outside AsyncLocalStorage scope.
|
|
156
|
+
const capturedContext = this.context;
|
|
157
|
+
await projectContextStore.run(this.context, async () => {
|
|
158
|
+
const mcpSubService = McpSubscriptionService.getInstance();
|
|
159
|
+
mcpSubService.setNotificationHandler(async (subscription, content) => {
|
|
160
|
+
await projectContextStore.run(capturedContext, async () => {
|
|
161
|
+
await deliverMcpNotification(subscription, content);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
await mcpSubService.initialize();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Initialize and set local report store on context for project-scoped storage
|
|
168
|
+
this.localReportStore.initialize(this.metadataPath);
|
|
169
|
+
this.context.localReportStore = this.localReportStore;
|
|
170
|
+
|
|
171
|
+
// NOTE: Nudge whitelist is initialized at daemon level (user-scoped, not project-scoped).
|
|
172
|
+
// See Daemon.ts step 6d.
|
|
173
|
+
|
|
174
|
+
// Initialize conversation store with project path and agent pubkeys
|
|
175
|
+
const agentPubkeys = Array.from(this.context.agents.values()).map(a => a.pubkey);
|
|
176
|
+
ConversationStore.initialize(this.metadataPath, agentPubkeys);
|
|
177
|
+
|
|
178
|
+
// Reconcile any orphaned RALs from a previous daemon run
|
|
179
|
+
await this.reconcileOrphanedRals();
|
|
180
|
+
|
|
181
|
+
// Warm user profile cache for whitelisted pubkeys and project owner
|
|
182
|
+
// This ensures getNameSync() returns real names instead of shortened pubkeys
|
|
183
|
+
// for message attribution in delegations.
|
|
184
|
+
// Must run within projectContextStore.run() since PubkeyService.getAgentSlug
|
|
185
|
+
// needs project context to filter out agent pubkeys.
|
|
186
|
+
await projectContextStore.run(this.context, async () => {
|
|
187
|
+
await this.warmUserProfileCache();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Initialize backend pubkey cache for the pubkey gate.
|
|
191
|
+
// Must happen before EventHandler is initialized so that
|
|
192
|
+
// isTrustedEventSync() can recognize backend-signed events
|
|
193
|
+
// without an async fallback (fail-closed gate).
|
|
194
|
+
await getTrustPubkeyService().initializeBackendPubkeyCache();
|
|
195
|
+
|
|
196
|
+
// Initialize event handler
|
|
197
|
+
this.eventHandler = new EventHandler();
|
|
198
|
+
await this.eventHandler.initialize();
|
|
199
|
+
|
|
200
|
+
// Start status publisher
|
|
201
|
+
this.statusPublisher = new ProjectStatusService();
|
|
202
|
+
const context = this.context;
|
|
203
|
+
context.statusPublisher = this.statusPublisher;
|
|
204
|
+
await projectContextStore.run(context, async () => {
|
|
205
|
+
await this.statusPublisher?.startPublishing(
|
|
206
|
+
this.projectBasePath,
|
|
207
|
+
context
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Start operations status publisher (uses RALRegistry for streaming-only semantics)
|
|
212
|
+
// Pass projectId and context for multi-project isolation in daemon mode
|
|
213
|
+
this.operationsStatusPublisher = new OperationsStatusService(
|
|
214
|
+
RALRegistry.getInstance(),
|
|
215
|
+
this.projectId,
|
|
216
|
+
this.context
|
|
217
|
+
);
|
|
218
|
+
this.operationsStatusPublisher.start();
|
|
219
|
+
|
|
220
|
+
this.isRunning = true;
|
|
221
|
+
this.startTime = new Date();
|
|
222
|
+
|
|
223
|
+
logger.info(`Project runtime started successfully: ${this.projectId}`, {
|
|
224
|
+
agentCount: this.context.agents.size,
|
|
225
|
+
pmPubkey: this.context.projectManager?.pubkey?.slice(0, 8),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
console.log(chalk.green(`✅ Project started: ${chalk.bold(projectTitle)}`));
|
|
229
|
+
console.log(
|
|
230
|
+
chalk.gray(` Agents: ${this.context.agents.size} | Path: ${this.projectBasePath}`)
|
|
231
|
+
);
|
|
232
|
+
console.log();
|
|
233
|
+
} catch (error) {
|
|
234
|
+
// Release prefix store reference if we acquired one during startup
|
|
235
|
+
if (this.prefixStoreInitialized) {
|
|
236
|
+
await prefixKVStore.close();
|
|
237
|
+
this.prefixStoreInitialized = false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
241
|
+
console.error(chalk.red(`❌ Failed to start project: ${chalk.bold(projectTitle)}`));
|
|
242
|
+
console.error(chalk.red(` ${errorMessage}`));
|
|
243
|
+
|
|
244
|
+
logger.error(`Failed to start project runtime: ${this.projectId}`, {
|
|
245
|
+
error: errorMessage,
|
|
246
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
247
|
+
});
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Handle an incoming event
|
|
254
|
+
*/
|
|
255
|
+
async handleEvent(event: NDKEvent): Promise<void> {
|
|
256
|
+
if (!this.isRunning) {
|
|
257
|
+
throw new Error(`Project runtime not running: ${this.projectId}`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (!this.context) {
|
|
261
|
+
throw new Error(`Project context not initialized: ${this.projectId}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Update stats
|
|
265
|
+
this.lastEventTime = new Date();
|
|
266
|
+
this.eventCount++;
|
|
267
|
+
|
|
268
|
+
// Set project.dtag on active span for trace filtering
|
|
269
|
+
const activeSpan = trace.getActiveSpan();
|
|
270
|
+
if (activeSpan) {
|
|
271
|
+
activeSpan.setAttribute("project.dtag", this.dTag);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Run event handler with the project context
|
|
275
|
+
// AsyncLocalStorage ensures all async operations within this scope
|
|
276
|
+
// have access to the correct project context
|
|
277
|
+
await projectContextStore.run(this.context, async () => {
|
|
278
|
+
if (this.eventHandler) {
|
|
279
|
+
await this.eventHandler.handleEvent(event);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Stop the project runtime
|
|
286
|
+
*/
|
|
287
|
+
async stop(): Promise<void> {
|
|
288
|
+
if (!this.isRunning) {
|
|
289
|
+
logger.warn(`Project runtime already stopped: ${this.projectId}`);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const projectTitle = this.project.tagValue("title") || "Untitled";
|
|
294
|
+
console.log(chalk.yellow(`🛑 Stopping project: ${chalk.bold(projectTitle)}`));
|
|
295
|
+
|
|
296
|
+
trace.getActiveSpan()?.addEvent("project_runtime.stopping", {
|
|
297
|
+
"project.id": this.projectId,
|
|
298
|
+
"uptime_ms": this.startTime ? Date.now() - this.startTime.getTime() : 0,
|
|
299
|
+
"events.processed": this.eventCount,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Stop status publisher
|
|
303
|
+
if (this.statusPublisher) {
|
|
304
|
+
process.stdout.write(chalk.gray(" Stopping status publisher..."));
|
|
305
|
+
await this.statusPublisher.stopPublishing();
|
|
306
|
+
this.statusPublisher = null;
|
|
307
|
+
console.log(chalk.gray(" done"));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Stop operations status publisher
|
|
311
|
+
if (this.operationsStatusPublisher) {
|
|
312
|
+
process.stdout.write(chalk.gray(" Stopping operations status..."));
|
|
313
|
+
this.operationsStatusPublisher.stop();
|
|
314
|
+
this.operationsStatusPublisher = null;
|
|
315
|
+
console.log(chalk.gray(" done"));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Cleanup event handler
|
|
319
|
+
if (this.eventHandler) {
|
|
320
|
+
process.stdout.write(chalk.gray(" Cleaning up event handler..."));
|
|
321
|
+
await this.eventHandler.cleanup();
|
|
322
|
+
this.eventHandler = null;
|
|
323
|
+
console.log(chalk.gray(" done"));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Shutdown MCP subscription service
|
|
327
|
+
try {
|
|
328
|
+
const mcpSubService = McpSubscriptionService.getInstance();
|
|
329
|
+
await mcpSubService.shutdown();
|
|
330
|
+
} catch {
|
|
331
|
+
// Service may not be initialized
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Save conversation state
|
|
335
|
+
process.stdout.write(chalk.gray(" Saving conversations..."));
|
|
336
|
+
await ConversationStore.cleanup();
|
|
337
|
+
console.log(chalk.gray(" done"));
|
|
338
|
+
|
|
339
|
+
// Reset local report store
|
|
340
|
+
process.stdout.write(chalk.gray(" Resetting report store..."));
|
|
341
|
+
this.localReportStore.reset();
|
|
342
|
+
console.log(chalk.gray(" done"));
|
|
343
|
+
|
|
344
|
+
// Release our reference to the prefix KV store (but don't close it -
|
|
345
|
+
// it's a daemon-global resource that outlives individual project runtimes)
|
|
346
|
+
process.stdout.write(chalk.gray(" Releasing storage..."));
|
|
347
|
+
await prefixKVStore.close();
|
|
348
|
+
console.log(chalk.gray(" done"));
|
|
349
|
+
|
|
350
|
+
// Clear context
|
|
351
|
+
this.context = null;
|
|
352
|
+
|
|
353
|
+
this.isRunning = false;
|
|
354
|
+
|
|
355
|
+
logger.info(`Project runtime stopped: ${this.projectId}`);
|
|
356
|
+
|
|
357
|
+
const uptime = this.startTime
|
|
358
|
+
? Math.round((Date.now() - this.startTime.getTime()) / 1000)
|
|
359
|
+
: 0;
|
|
360
|
+
console.log(chalk.green(`✅ Project stopped: ${chalk.bold(projectTitle)}`));
|
|
361
|
+
console.log(chalk.gray(` Uptime: ${uptime}s | Events processed: ${this.eventCount}`));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Get runtime status
|
|
366
|
+
*/
|
|
367
|
+
getStatus(): {
|
|
368
|
+
isRunning: boolean;
|
|
369
|
+
projectId: string;
|
|
370
|
+
title: string;
|
|
371
|
+
startTime: Date | null;
|
|
372
|
+
lastEventTime: Date | null;
|
|
373
|
+
eventCount: number;
|
|
374
|
+
agentCount: number;
|
|
375
|
+
} {
|
|
376
|
+
return {
|
|
377
|
+
isRunning: this.isRunning,
|
|
378
|
+
projectId: this.projectId,
|
|
379
|
+
title: this.project.tagValue("title") || "Untitled",
|
|
380
|
+
startTime: this.startTime,
|
|
381
|
+
lastEventTime: this.lastEventTime,
|
|
382
|
+
eventCount: this.eventCount,
|
|
383
|
+
agentCount: this.context?.agents.size || 0,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Get the project context (if running)
|
|
389
|
+
*/
|
|
390
|
+
getContext(): ProjectContext | null {
|
|
391
|
+
return this.context;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Check if runtime is running
|
|
396
|
+
*/
|
|
397
|
+
isActive(): boolean {
|
|
398
|
+
return this.isRunning;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Initialize MCP tools from the project event
|
|
403
|
+
* Extracts MCP tool event IDs from "mcp" tags, fetches and installs them
|
|
404
|
+
*/
|
|
405
|
+
private async initializeMCPTools(): Promise<void> {
|
|
406
|
+
try {
|
|
407
|
+
// Extract MCP tool event IDs from the project
|
|
408
|
+
const mcpEventIds = this.project.tags
|
|
409
|
+
.filter((tag) => tag[0] === "mcp" && tag[1])
|
|
410
|
+
.map((tag) => tag[1])
|
|
411
|
+
.filter((id): id is string => typeof id === "string");
|
|
412
|
+
|
|
413
|
+
trace.getActiveSpan()?.addEvent("project_runtime.mcp_tools_found", {
|
|
414
|
+
"mcp.count": mcpEventIds.length,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
if (mcpEventIds.length === 0) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const ndk = getNDK();
|
|
422
|
+
const installedCount = { success: 0, failed: 0 };
|
|
423
|
+
|
|
424
|
+
// Fetch and install each MCP tool
|
|
425
|
+
for (const eventId of mcpEventIds) {
|
|
426
|
+
try {
|
|
427
|
+
trace.getActiveSpan()?.addEvent("project_runtime.mcp_fetching", {
|
|
428
|
+
"mcp.event_id": eventId.substring(0, 12),
|
|
429
|
+
});
|
|
430
|
+
const mcpEvent = await ndk.fetchEvent(eventId);
|
|
431
|
+
|
|
432
|
+
if (!mcpEvent) {
|
|
433
|
+
logger.warn(
|
|
434
|
+
`[ProjectRuntime] MCP tool event not found: ${eventId.substring(0, 12)}`
|
|
435
|
+
);
|
|
436
|
+
installedCount.failed++;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const mcpTool = NDKMCPTool.from(mcpEvent);
|
|
441
|
+
trace.getActiveSpan()?.addEvent("project_runtime.mcp_installing", {
|
|
442
|
+
"mcp.name": mcpTool.name ?? "unnamed",
|
|
443
|
+
"mcp.event_id": eventId.substring(0, 12),
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
await installMCPServerFromEvent(this.metadataPath, mcpTool);
|
|
447
|
+
installedCount.success++;
|
|
448
|
+
|
|
449
|
+
trace.getActiveSpan()?.addEvent("project_runtime.mcp_installed", {
|
|
450
|
+
"mcp.name": mcpTool.name ?? "",
|
|
451
|
+
"mcp.slug": mcpTool.slug ?? "",
|
|
452
|
+
});
|
|
453
|
+
} catch (error) {
|
|
454
|
+
logger.error("[ProjectRuntime] Failed to install MCP tool", {
|
|
455
|
+
eventId: eventId.substring(0, 12),
|
|
456
|
+
error: error instanceof Error ? error.message : String(error),
|
|
457
|
+
});
|
|
458
|
+
installedCount.failed++;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
trace.getActiveSpan()?.addEvent("project_runtime.mcp_installation_complete", {
|
|
463
|
+
"mcp.total": mcpEventIds.length,
|
|
464
|
+
"mcp.success": installedCount.success,
|
|
465
|
+
"mcp.failed": installedCount.failed,
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// Initialize MCP service if any tools were installed
|
|
469
|
+
if (installedCount.success > 0) {
|
|
470
|
+
await this.mcpManager.initialize(this.metadataPath, this.projectBasePath);
|
|
471
|
+
|
|
472
|
+
const runningServers = this.mcpManager.getRunningServers();
|
|
473
|
+
const availableTools = Object.keys(this.mcpManager.getCachedTools());
|
|
474
|
+
|
|
475
|
+
trace.getActiveSpan()?.addEvent("project_runtime.mcp_service_initialized", {
|
|
476
|
+
"mcp.running_servers": runningServers.length,
|
|
477
|
+
"mcp.available_tools": availableTools.length,
|
|
478
|
+
});
|
|
479
|
+
} else {
|
|
480
|
+
logger.warn(
|
|
481
|
+
"[ProjectRuntime] No MCP tools were successfully installed, skipping MCP service initialization"
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
} catch (error) {
|
|
485
|
+
logger.error("[ProjectRuntime] Failed to initialize MCP tools", {
|
|
486
|
+
error: error instanceof Error ? error.message : String(error),
|
|
487
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
488
|
+
});
|
|
489
|
+
// Don't throw - allow project to start even if MCP initialization fails
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Warm the user profile cache for whitelisted pubkeys and project owner.
|
|
495
|
+
* This ensures that getNameSync() returns real user names instead of shortened pubkeys
|
|
496
|
+
* when attributing messages in delegations.
|
|
497
|
+
*/
|
|
498
|
+
private async warmUserProfileCache(): Promise<void> {
|
|
499
|
+
try {
|
|
500
|
+
const { config: loadedConfig } = await config.loadConfig();
|
|
501
|
+
const pubkeysToWarm: Set<string> = new Set();
|
|
502
|
+
|
|
503
|
+
// Add whitelisted pubkeys
|
|
504
|
+
const whitelistedPubkeys = loadedConfig.whitelistedPubkeys ?? [];
|
|
505
|
+
for (const pk of whitelistedPubkeys) {
|
|
506
|
+
if (pk) pubkeysToWarm.add(pk);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Add project owner pubkey
|
|
510
|
+
if (this.project.pubkey) {
|
|
511
|
+
pubkeysToWarm.add(this.project.pubkey);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (pubkeysToWarm.size === 0) {
|
|
515
|
+
logger.debug("[ProjectRuntime] No user pubkeys to warm");
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
trace.getActiveSpan()?.addEvent("project_runtime.warming_user_profiles", {
|
|
520
|
+
"profiles.count": pubkeysToWarm.size,
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
const pubkeyService = getPubkeyService();
|
|
524
|
+
const results = await pubkeyService.warmUserProfiles(Array.from(pubkeysToWarm));
|
|
525
|
+
|
|
526
|
+
logger.debug("[ProjectRuntime] Warmed user profile cache", {
|
|
527
|
+
projectId: this.projectId,
|
|
528
|
+
count: results.size,
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
trace.getActiveSpan()?.addEvent("project_runtime.user_profiles_warmed", {
|
|
532
|
+
"profiles.warmed": results.size,
|
|
533
|
+
});
|
|
534
|
+
} catch (error) {
|
|
535
|
+
// Don't block startup if profile warming fails
|
|
536
|
+
logger.warn("[ProjectRuntime] Failed to warm user profile cache", {
|
|
537
|
+
projectId: this.projectId,
|
|
538
|
+
error: error instanceof Error ? error.message : String(error),
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Scan all persisted conversations and reconcile orphaned RALs.
|
|
545
|
+
* An orphaned RAL is one that's marked as "active" in ConversationStore
|
|
546
|
+
* but doesn't exist in RALRegistry (because daemon was restarted).
|
|
547
|
+
*/
|
|
548
|
+
private async reconcileOrphanedRals(): Promise<void> {
|
|
549
|
+
const conversationsDir = path.join(this.metadataPath, "conversations");
|
|
550
|
+
|
|
551
|
+
let files: string[];
|
|
552
|
+
try {
|
|
553
|
+
files = await fs.readdir(conversationsDir);
|
|
554
|
+
} catch {
|
|
555
|
+
return; // No conversations directory yet
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const ralRegistry = RALRegistry.getInstance();
|
|
559
|
+
let totalReconciled = 0;
|
|
560
|
+
|
|
561
|
+
for (const file of files) {
|
|
562
|
+
if (!file.endsWith(".json")) continue;
|
|
563
|
+
|
|
564
|
+
const conversationId = file.replace(".json", "");
|
|
565
|
+
const store = ConversationStore.getOrLoad(conversationId);
|
|
566
|
+
|
|
567
|
+
const allActiveRals = store.getAllActiveRals();
|
|
568
|
+
let modified = false;
|
|
569
|
+
|
|
570
|
+
for (const [agentPubkey, ralNumbers] of allActiveRals) {
|
|
571
|
+
for (const ralNumber of ralNumbers) {
|
|
572
|
+
// RALRegistry is empty after restart - any active RAL in ConversationStore is orphaned
|
|
573
|
+
const exists = ralRegistry.getRAL(agentPubkey, conversationId, ralNumber);
|
|
574
|
+
if (!exists) {
|
|
575
|
+
logger.info(`[ProjectRuntime] Reconciling orphaned RAL #${ralNumber}`, {
|
|
576
|
+
conversationId: conversationId.substring(0, 8),
|
|
577
|
+
agentPubkey: agentPubkey.substring(0, 8),
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
store.addMessage({
|
|
581
|
+
pubkey: agentPubkey,
|
|
582
|
+
ral: ralNumber,
|
|
583
|
+
content: `⚠️ RAL #${ralNumber} was interrupted (daemon restart). Work may be incomplete.`,
|
|
584
|
+
messageType: "text",
|
|
585
|
+
});
|
|
586
|
+
store.completeRal(agentPubkey, ralNumber);
|
|
587
|
+
modified = true;
|
|
588
|
+
totalReconciled++;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (modified) {
|
|
594
|
+
await store.save();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (totalReconciled > 0) {
|
|
599
|
+
logger.info(`[ProjectRuntime] Reconciled ${totalReconciled} orphaned RAL(s)`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Verify supervision system health at startup.
|
|
605
|
+
* Ensures heuristics are registered and the supervision system is properly configured.
|
|
606
|
+
* This is a fail-fast check that prevents the daemon from running without supervision.
|
|
607
|
+
*
|
|
608
|
+
* Uses centralized health check for consistent fail-closed semantics across all entry points.
|
|
609
|
+
*/
|
|
610
|
+
private async verifySupervisionHealth(): Promise<void> {
|
|
611
|
+
const tracer = trace.getTracer("tenex.project-runtime");
|
|
612
|
+
|
|
613
|
+
return tracer.startActiveSpan("tenex.supervision.health_check", async (span) => {
|
|
614
|
+
span.setAttribute("project.id", this.projectId);
|
|
615
|
+
|
|
616
|
+
// Ensure heuristics are registered before checking health
|
|
617
|
+
registerDefaultHeuristics();
|
|
618
|
+
|
|
619
|
+
// Use centralized health check for consistent validation
|
|
620
|
+
const healthResult = checkSupervisionHealth();
|
|
621
|
+
|
|
622
|
+
span.setAttributes({
|
|
623
|
+
"supervision.registry_size": healthResult.registrySize,
|
|
624
|
+
"supervision.heuristic_ids": healthResult.heuristicIds.join(","),
|
|
625
|
+
"supervision.post_completion_count": healthResult.postCompletionCount,
|
|
626
|
+
"supervision.healthy": healthResult.healthy,
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
if (!healthResult.healthy) {
|
|
630
|
+
const errorMessage = `[ProjectRuntime] ${healthResult.errorMessage}`;
|
|
631
|
+
|
|
632
|
+
logger.error(errorMessage);
|
|
633
|
+
span.recordException(new Error(errorMessage));
|
|
634
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: healthResult.errorMessage });
|
|
635
|
+
span.end();
|
|
636
|
+
|
|
637
|
+
throw new Error(errorMessage);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
span.addEvent("supervision.health_check_passed", {
|
|
641
|
+
"heuristics.count": healthResult.registrySize,
|
|
642
|
+
"heuristics.post_completion_count": healthResult.postCompletionCount,
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
646
|
+
|
|
647
|
+
logger.info("[ProjectRuntime] Supervision system health check passed", {
|
|
648
|
+
projectId: this.projectId,
|
|
649
|
+
heuristicCount: healthResult.registrySize,
|
|
650
|
+
heuristicIds: healthResult.heuristicIds,
|
|
651
|
+
postCompletionCount: healthResult.postCompletionCount,
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
span.end();
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
}
|