@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,728 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConversationRegistry - Global registry for conversation stores
|
|
3
|
+
*
|
|
4
|
+
* This singleton manages the lifecycle of ConversationStore instances:
|
|
5
|
+
* - Loading and caching conversation stores
|
|
6
|
+
* - Event caching for fast lookup
|
|
7
|
+
* - Project-level initialization (multi-project safe)
|
|
8
|
+
* - Cross-project conversation discovery
|
|
9
|
+
*
|
|
10
|
+
* Multi-project support:
|
|
11
|
+
* initialize() accumulates per-project configs instead of overwriting.
|
|
12
|
+
* Methods that need a project ID resolve it via three-tier strategy:
|
|
13
|
+
* 1. Explicit projectId parameter (if passed)
|
|
14
|
+
* 2. AsyncLocalStorage projectContextStore lookup
|
|
15
|
+
* 3. Legacy fallback (last initialized) with warning log
|
|
16
|
+
*
|
|
17
|
+
* The heavy lifting is delegated to individual ConversationStore instances.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { existsSync, readdirSync } from "fs";
|
|
21
|
+
import { basename, dirname, join } from "path";
|
|
22
|
+
import { getTenexBasePath } from "@/constants";
|
|
23
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
24
|
+
import { logger } from "@/utils/logger";
|
|
25
|
+
// Import directly from the module file (not the barrel) to avoid circular
|
|
26
|
+
// dependency: barrel re-exports ProjectContext → @/agents → ConversationStore
|
|
27
|
+
// → ConversationRegistry (this file), which would trigger a ReferenceError.
|
|
28
|
+
import { projectContextStore } from "@/services/projects/ProjectContextStore";
|
|
29
|
+
import type { ConversationMetadata } from "./types";
|
|
30
|
+
import type { ConversationStore } from "./ConversationStore";
|
|
31
|
+
// Note: FullEventId type is available via @/types/event-ids for future typed method signatures
|
|
32
|
+
import {
|
|
33
|
+
listConversationIdsFromDiskForProject,
|
|
34
|
+
listProjectIdsFromDisk,
|
|
35
|
+
readConversationPreviewForProject,
|
|
36
|
+
readLightweightMetadata,
|
|
37
|
+
readMessagesFromDisk,
|
|
38
|
+
} from "./ConversationDiskReader";
|
|
39
|
+
import {
|
|
40
|
+
getIndexManager,
|
|
41
|
+
parseQuery,
|
|
42
|
+
search as searchIndex,
|
|
43
|
+
type AdvancedSearchResult,
|
|
44
|
+
type RawSearchInput,
|
|
45
|
+
} from "./search";
|
|
46
|
+
import { isHexPrefix, resolvePrefixToId, PREFIX_LENGTH } from "@/utils/nostr-entity-parser";
|
|
47
|
+
import { prefixKVStore } from "@/services/storage";
|
|
48
|
+
|
|
49
|
+
// ConversationStore class is registered from ConversationStore module to avoid circular imports.
|
|
50
|
+
let ConversationStoreClass: typeof ConversationStore | null = null;
|
|
51
|
+
|
|
52
|
+
function getConversationStoreClass(): typeof ConversationStore {
|
|
53
|
+
if (!ConversationStoreClass) {
|
|
54
|
+
throw new Error("ConversationStore class not registered. Ensure ConversationStore module is loaded.");
|
|
55
|
+
}
|
|
56
|
+
return ConversationStoreClass;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Per-project configuration stored by the registry.
|
|
61
|
+
*/
|
|
62
|
+
interface ProjectRegistryConfig {
|
|
63
|
+
agentPubkeys: Set<string>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Singleton registry for managing conversation stores
|
|
68
|
+
*/
|
|
69
|
+
class ConversationRegistryImpl {
|
|
70
|
+
private stores: Map<string, ConversationStore> = new Map();
|
|
71
|
+
private eventCache: Map<string, NDKEvent> = new Map();
|
|
72
|
+
private _basePath: string = join(getTenexBasePath(), "projects");
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Per-project configurations keyed by projectId (dTag).
|
|
76
|
+
* Accumulated by initialize() — never overwritten.
|
|
77
|
+
*/
|
|
78
|
+
private _projectConfigs: Map<string, ProjectRegistryConfig> = new Map();
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Union of all agent pubkeys across all initialized projects.
|
|
82
|
+
* Maintained alongside _projectConfigs for efficient lookup.
|
|
83
|
+
*/
|
|
84
|
+
private _allAgentPubkeys: Set<string> = new Set();
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Legacy fallback: the last projectId set via initialize().
|
|
88
|
+
* Used only when AsyncLocalStorage context is unavailable (backward compat).
|
|
89
|
+
*/
|
|
90
|
+
private _legacyProjectId: string | null = null;
|
|
91
|
+
|
|
92
|
+
get basePath(): string {
|
|
93
|
+
return this._basePath;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get the current project ID via three-tier resolution.
|
|
98
|
+
* Prefer resolveProjectId() for new code paths.
|
|
99
|
+
*/
|
|
100
|
+
get projectId(): string | null {
|
|
101
|
+
return this.resolveProjectId();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get agent pubkeys for the current resolved project.
|
|
106
|
+
* Falls back to all known agent pubkeys if no project can be resolved.
|
|
107
|
+
*/
|
|
108
|
+
get agentPubkeys(): Set<string> {
|
|
109
|
+
return this.getAgentPubkeysForProject(this.resolveProjectId());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setConversationStoreClass(StoreClass: typeof ConversationStore): void {
|
|
113
|
+
ConversationStoreClass = StoreClass;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Resolve the current project ID via three-tier strategy:
|
|
118
|
+
* 1. Explicit projectId parameter (if passed)
|
|
119
|
+
* 2. AsyncLocalStorage projectContextStore lookup
|
|
120
|
+
* 3. Legacy fallback (last initialized) with warning log
|
|
121
|
+
*
|
|
122
|
+
* @param explicitProjectId - Optional explicit project ID to use directly
|
|
123
|
+
* @returns The resolved project ID, or null if none can be determined
|
|
124
|
+
*/
|
|
125
|
+
resolveProjectId(explicitProjectId?: string): string | null {
|
|
126
|
+
// Tier 1: Explicit parameter
|
|
127
|
+
if (explicitProjectId) {
|
|
128
|
+
return explicitProjectId;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Tier 2: AsyncLocalStorage context
|
|
132
|
+
try {
|
|
133
|
+
const context = projectContextStore.getContext();
|
|
134
|
+
if (context) {
|
|
135
|
+
const dTag = context.project.tagValue("d");
|
|
136
|
+
if (dTag && this._projectConfigs.has(dTag)) {
|
|
137
|
+
return dTag;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
logger.debug("[ConversationRegistry] Failed to read AsyncLocalStorage context", { error });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Tier 3: Legacy fallback with warning
|
|
145
|
+
if (this._legacyProjectId) {
|
|
146
|
+
// Only warn if there are multiple projects (single project is expected)
|
|
147
|
+
if (this._projectConfigs.size > 1) {
|
|
148
|
+
logger.warn(
|
|
149
|
+
"[ConversationRegistry] Using legacy projectId fallback — " +
|
|
150
|
+
"this may resolve to the wrong project in multi-project mode",
|
|
151
|
+
{ projectId: this._legacyProjectId, knownProjects: this._projectConfigs.size }
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return this._legacyProjectId;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Rebuild the union of all agent pubkeys from all project configs.
|
|
162
|
+
* Called after any mutation to _projectConfigs to keep _allAgentPubkeys in sync.
|
|
163
|
+
*/
|
|
164
|
+
private rebuildAllAgentPubkeys(): void {
|
|
165
|
+
this._allAgentPubkeys = new Set();
|
|
166
|
+
for (const config of this._projectConfigs.values()) {
|
|
167
|
+
for (const pk of config.agentPubkeys) {
|
|
168
|
+
this._allAgentPubkeys.add(pk);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get the agent pubkeys for a specific resolved project ID.
|
|
175
|
+
* Returns the project-specific set if found, otherwise the union of all.
|
|
176
|
+
*/
|
|
177
|
+
private getAgentPubkeysForProject(projectId: string | null): Set<string> {
|
|
178
|
+
if (projectId) {
|
|
179
|
+
const config = this._projectConfigs.get(projectId);
|
|
180
|
+
if (config) {
|
|
181
|
+
return config.agentPubkeys;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return this._allAgentPubkeys;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Resolve a conversation ID that may be a 12-char prefix to a full 64-char ID.
|
|
189
|
+
* Returns the input as-is if it's already a full ID or if resolution fails.
|
|
190
|
+
*
|
|
191
|
+
* @param conversationId - Either a full 64-char hex ID or a 12-char hex prefix
|
|
192
|
+
* @returns The full 64-char ID if resolved, otherwise the original input
|
|
193
|
+
*/
|
|
194
|
+
private resolveConversationId(conversationId: string): string {
|
|
195
|
+
// Already a full ID (64 hex chars)
|
|
196
|
+
if (/^[0-9a-fA-F]{64}$/.test(conversationId)) {
|
|
197
|
+
return conversationId.toLowerCase();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Check if it's a 12-char hex prefix
|
|
201
|
+
if (isHexPrefix(conversationId)) {
|
|
202
|
+
const resolved = resolvePrefixToId(conversationId);
|
|
203
|
+
if (resolved) {
|
|
204
|
+
logger.debug(`[ConversationRegistry] Resolved prefix ${conversationId} to ${resolved.substring(0, 12)}...`);
|
|
205
|
+
return resolved;
|
|
206
|
+
}
|
|
207
|
+
// Fall through to return original if resolution fails
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Return as-is (may be invalid, but let caller handle)
|
|
211
|
+
return conversationId;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Initialize the registry for a project.
|
|
216
|
+
* In multi-project mode (daemon), this is called once per project.
|
|
217
|
+
* Accumulates per-project configs rather than overwriting.
|
|
218
|
+
*/
|
|
219
|
+
initialize(metadataPath: string, agentPubkeys?: Iterable<string>): void {
|
|
220
|
+
this._basePath = dirname(metadataPath);
|
|
221
|
+
const projectId = basename(metadataPath);
|
|
222
|
+
const pubkeys = new Set(agentPubkeys ?? []);
|
|
223
|
+
|
|
224
|
+
// Accumulate per-project config
|
|
225
|
+
this._projectConfigs.set(projectId, { agentPubkeys: pubkeys });
|
|
226
|
+
|
|
227
|
+
// Rebuild the union of all agent pubkeys
|
|
228
|
+
this.rebuildAllAgentPubkeys();
|
|
229
|
+
|
|
230
|
+
// Track last initialized for legacy fallback
|
|
231
|
+
this._legacyProjectId = projectId;
|
|
232
|
+
|
|
233
|
+
logger.info(`[ConversationRegistry] Initialized for project ${projectId}`, {
|
|
234
|
+
totalProjects: this._projectConfigs.size,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get or load a conversation store by ID.
|
|
240
|
+
* Loads from disk if not already in memory.
|
|
241
|
+
* Supports 12-char hex prefix lookups via PrefixKVStore.
|
|
242
|
+
*/
|
|
243
|
+
getOrLoad(conversationId: string): ConversationStore {
|
|
244
|
+
// Resolve prefix to full ID if needed (consistent with get/has)
|
|
245
|
+
const resolvedId = this.resolveConversationId(conversationId);
|
|
246
|
+
|
|
247
|
+
let store = this.stores.get(resolvedId);
|
|
248
|
+
if (!store) {
|
|
249
|
+
const currentProjectId = this.resolveProjectId();
|
|
250
|
+
if (!currentProjectId) {
|
|
251
|
+
throw new Error("ConversationRegistry.initialize() must be called before getOrLoad()");
|
|
252
|
+
}
|
|
253
|
+
const StoreClass = getConversationStoreClass();
|
|
254
|
+
store = new StoreClass(this._basePath);
|
|
255
|
+
store.load(currentProjectId, resolvedId);
|
|
256
|
+
this.stores.set(resolvedId, store);
|
|
257
|
+
}
|
|
258
|
+
return store;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get a conversation store if it exists in memory or on disk.
|
|
263
|
+
* Returns undefined if conversation doesn't exist.
|
|
264
|
+
* Supports 12-char hex prefix lookups via PrefixKVStore.
|
|
265
|
+
*/
|
|
266
|
+
get(conversationId: string): ConversationStore | undefined {
|
|
267
|
+
// Resolve prefix to full ID if needed
|
|
268
|
+
const resolvedId = this.resolveConversationId(conversationId);
|
|
269
|
+
|
|
270
|
+
const cached = this.stores.get(resolvedId);
|
|
271
|
+
if (cached) return cached;
|
|
272
|
+
|
|
273
|
+
// Try current project first
|
|
274
|
+
const currentProjectId = this.resolveProjectId();
|
|
275
|
+
if (currentProjectId) {
|
|
276
|
+
const StoreClass = getConversationStoreClass();
|
|
277
|
+
const store = new StoreClass(this._basePath);
|
|
278
|
+
try {
|
|
279
|
+
store.load(currentProjectId, resolvedId);
|
|
280
|
+
if (store.getAllMessages().length > 0) {
|
|
281
|
+
this.stores.set(resolvedId, store);
|
|
282
|
+
return store;
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
// Store doesn't exist
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Search other projects
|
|
290
|
+
const otherProjectId = this.findProjectForConversation(resolvedId, currentProjectId);
|
|
291
|
+
if (otherProjectId) {
|
|
292
|
+
const StoreClass = getConversationStoreClass();
|
|
293
|
+
const store = new StoreClass(this._basePath);
|
|
294
|
+
try {
|
|
295
|
+
store.load(otherProjectId, resolvedId);
|
|
296
|
+
if (store.getAllMessages().length > 0) {
|
|
297
|
+
this.stores.set(resolvedId, store);
|
|
298
|
+
logger.debug(`[ConversationRegistry] Found conversation ${resolvedId.substring(0, 8)} in project ${otherProjectId}`);
|
|
299
|
+
return store;
|
|
300
|
+
}
|
|
301
|
+
} catch {
|
|
302
|
+
// Store doesn't exist
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Find which project contains a conversation.
|
|
311
|
+
*/
|
|
312
|
+
private findProjectForConversation(conversationId: string, skipProjectId?: string | null): string | undefined {
|
|
313
|
+
try {
|
|
314
|
+
if (!existsSync(this._basePath)) return undefined;
|
|
315
|
+
|
|
316
|
+
const projectDirs = readdirSync(this._basePath);
|
|
317
|
+
for (const projectDir of projectDirs) {
|
|
318
|
+
if (projectDir === skipProjectId) continue;
|
|
319
|
+
if (projectDir === "metadata") continue;
|
|
320
|
+
|
|
321
|
+
const conversationFile = join(
|
|
322
|
+
this._basePath,
|
|
323
|
+
projectDir,
|
|
324
|
+
"conversations",
|
|
325
|
+
`${conversationId}.json`
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
if (existsSync(conversationFile)) {
|
|
329
|
+
return projectDir;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
} catch {
|
|
333
|
+
// Error reading directories
|
|
334
|
+
}
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Check if a conversation exists.
|
|
340
|
+
*/
|
|
341
|
+
has(conversationId: string): boolean {
|
|
342
|
+
return this.get(conversationId) !== undefined;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Create a new conversation from an NDKEvent.
|
|
347
|
+
* Indexes the conversation ID in PrefixKVStore for prefix lookups.
|
|
348
|
+
* Uses three-tier project resolution to determine the correct project.
|
|
349
|
+
*/
|
|
350
|
+
async create(event: NDKEvent): Promise<ConversationStore> {
|
|
351
|
+
const eventId = event.id;
|
|
352
|
+
if (!eventId) {
|
|
353
|
+
throw new Error("Event must have an ID to create a conversation");
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const existing = this.stores.get(eventId);
|
|
357
|
+
if (existing) {
|
|
358
|
+
logger.debug(`Conversation ${eventId.substring(0, 8)} already exists`);
|
|
359
|
+
return existing;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const currentProjectId = this.resolveProjectId();
|
|
363
|
+
if (!currentProjectId) {
|
|
364
|
+
throw new Error("ConversationRegistry.initialize() must be called before create()");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const StoreClass = getConversationStoreClass();
|
|
368
|
+
const store = new StoreClass(this._basePath);
|
|
369
|
+
store.load(currentProjectId, eventId);
|
|
370
|
+
|
|
371
|
+
const projectAgentPubkeys = this.getAgentPubkeysForProject(currentProjectId);
|
|
372
|
+
const isFromAgent = projectAgentPubkeys.has(event.pubkey);
|
|
373
|
+
store.addEventMessage(event, isFromAgent);
|
|
374
|
+
|
|
375
|
+
this.eventCache.set(eventId, event);
|
|
376
|
+
|
|
377
|
+
if (event.content) {
|
|
378
|
+
store.setTitle(event.content.substring(0, 50) + (event.content.length > 50 ? "..." : ""));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
await store.save();
|
|
382
|
+
this.stores.set(eventId, store);
|
|
383
|
+
|
|
384
|
+
// Index conversation ID in PrefixKVStore for 12-char prefix lookups
|
|
385
|
+
//
|
|
386
|
+
// BACKFILL LIMITATION: Prefix indexing only happens on create().
|
|
387
|
+
// If the prefix store is empty (fresh install or data loss), pre-existing
|
|
388
|
+
// conversations won't be resolvable by prefix until the migration script
|
|
389
|
+
// (src/scripts/migrate-prefix-index.ts) is run, OR until those conversations
|
|
390
|
+
// receive a new message that triggers re-indexing via conversation events.
|
|
391
|
+
// This is acceptable because:
|
|
392
|
+
// 1. Most prefix lookups target recently-active conversations
|
|
393
|
+
// 2. Full 64-char IDs always work as a fallback
|
|
394
|
+
// 3. A migration script exists for backfilling if needed
|
|
395
|
+
if (prefixKVStore.isInitialized()) {
|
|
396
|
+
try {
|
|
397
|
+
await prefixKVStore.add(eventId);
|
|
398
|
+
} catch (error) {
|
|
399
|
+
logger.warn(`[ConversationRegistry] Failed to index conversation ${eventId.substring(0, PREFIX_LENGTH)} in PrefixKVStore`, { error });
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
logger.info(`Starting conversation ${eventId.substring(0, 8)} - "${event.content?.substring(0, 50)}..."`, {
|
|
404
|
+
projectId: currentProjectId,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
return store;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Find a conversation by an event ID it contains.
|
|
412
|
+
*/
|
|
413
|
+
findByEventId(eventId: string): ConversationStore | undefined {
|
|
414
|
+
for (const store of this.stores.values()) {
|
|
415
|
+
if (store.hasEventId(eventId)) {
|
|
416
|
+
return store;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Get all loaded conversation stores.
|
|
424
|
+
*/
|
|
425
|
+
getAll(): ConversationStore[] {
|
|
426
|
+
return Array.from(this.stores.values());
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Cache an NDKEvent.
|
|
431
|
+
*/
|
|
432
|
+
cacheEvent(event: NDKEvent): void {
|
|
433
|
+
if (event.id) {
|
|
434
|
+
this.eventCache.set(event.id, event);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Get a cached NDKEvent.
|
|
440
|
+
*/
|
|
441
|
+
getCachedEvent(eventId: string): NDKEvent | undefined {
|
|
442
|
+
return this.eventCache.get(eventId);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Add an event to a conversation.
|
|
447
|
+
*/
|
|
448
|
+
async addEvent(conversationId: string, event: NDKEvent): Promise<void> {
|
|
449
|
+
const store = this.getOrLoad(conversationId);
|
|
450
|
+
const currentProjectId = this.resolveProjectId();
|
|
451
|
+
const projectAgentPubkeys = this.getAgentPubkeysForProject(currentProjectId);
|
|
452
|
+
const isFromAgent = projectAgentPubkeys.has(event.pubkey);
|
|
453
|
+
store.addEventMessage(event, isFromAgent);
|
|
454
|
+
|
|
455
|
+
if (event.id) {
|
|
456
|
+
this.eventCache.set(event.id, event);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
await store.save();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Set conversation title.
|
|
464
|
+
*/
|
|
465
|
+
setConversationTitle(conversationId: string, title: string): void {
|
|
466
|
+
const store = this.get(conversationId);
|
|
467
|
+
if (store) {
|
|
468
|
+
store.setTitle(title);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Update conversation metadata.
|
|
474
|
+
*/
|
|
475
|
+
async updateConversationMetadata(
|
|
476
|
+
conversationId: string,
|
|
477
|
+
metadata: Partial<ConversationMetadata>
|
|
478
|
+
): Promise<void> {
|
|
479
|
+
const store = this.get(conversationId);
|
|
480
|
+
if (!store) {
|
|
481
|
+
throw new Error(`Conversation ${conversationId} not found`);
|
|
482
|
+
}
|
|
483
|
+
store.updateMetadata(metadata);
|
|
484
|
+
await store.save();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Archive a conversation (remove from memory).
|
|
489
|
+
*/
|
|
490
|
+
archive(conversationId: string): void {
|
|
491
|
+
const store = this.stores.get(conversationId);
|
|
492
|
+
if (store) {
|
|
493
|
+
for (const entry of store.getAllMessages()) {
|
|
494
|
+
if (entry.eventId) {
|
|
495
|
+
this.eventCache.delete(entry.eventId);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
this.stores.delete(conversationId);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Complete a conversation (save and archive).
|
|
504
|
+
*/
|
|
505
|
+
async complete(conversationId: string): Promise<void> {
|
|
506
|
+
const store = this.stores.get(conversationId);
|
|
507
|
+
if (store) {
|
|
508
|
+
await store.save();
|
|
509
|
+
for (const entry of store.getAllMessages()) {
|
|
510
|
+
if (entry.eventId) {
|
|
511
|
+
this.eventCache.delete(entry.eventId);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
this.stores.delete(conversationId);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Save all and clean up.
|
|
520
|
+
*/
|
|
521
|
+
async cleanup(): Promise<void> {
|
|
522
|
+
const promises: Promise<void>[] = [];
|
|
523
|
+
for (const store of this.stores.values()) {
|
|
524
|
+
promises.push(store.save());
|
|
525
|
+
}
|
|
526
|
+
await Promise.all(promises);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Search conversations by title (legacy in-memory search).
|
|
531
|
+
* @deprecated Use searchAdvanced for full-text search across all conversations.
|
|
532
|
+
*/
|
|
533
|
+
search(query: string): ConversationStore[] {
|
|
534
|
+
const results: ConversationStore[] = [];
|
|
535
|
+
const queryLower = query.toLowerCase();
|
|
536
|
+
for (const store of this.stores.values()) {
|
|
537
|
+
const title = store.getTitle();
|
|
538
|
+
if (title && title.toLowerCase().includes(queryLower)) {
|
|
539
|
+
results.push(store);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return results;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Advanced search across ALL conversations (not just in-memory).
|
|
547
|
+
* Supports full-text search on message content, agent filters, and date filters.
|
|
548
|
+
*
|
|
549
|
+
* @param input - Search input with query text and optional filters
|
|
550
|
+
* @param limit - Maximum number of results (default: 20)
|
|
551
|
+
* @returns AdvancedSearchResult with explicit success/error information
|
|
552
|
+
*/
|
|
553
|
+
searchAdvanced(input: RawSearchInput, limit: number = 20): AdvancedSearchResult {
|
|
554
|
+
const currentProjectId = this.resolveProjectId();
|
|
555
|
+
if (!currentProjectId) {
|
|
556
|
+
logger.warn("[ConversationRegistry] searchAdvanced called before initialization");
|
|
557
|
+
return {
|
|
558
|
+
success: false,
|
|
559
|
+
results: [],
|
|
560
|
+
error: "ConversationRegistry not initialized",
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
try {
|
|
565
|
+
// Parse and validate the query
|
|
566
|
+
const query = parseQuery(input);
|
|
567
|
+
|
|
568
|
+
// Get or create the index manager for this project
|
|
569
|
+
const indexManager = getIndexManager(this._basePath, currentProjectId);
|
|
570
|
+
|
|
571
|
+
// Get the index (loads from disk or rebuilds if needed)
|
|
572
|
+
const index = indexManager.getIndex();
|
|
573
|
+
|
|
574
|
+
// Perform the search
|
|
575
|
+
const results = searchIndex(query, index, limit);
|
|
576
|
+
|
|
577
|
+
logger.debug("[ConversationRegistry] Advanced search completed", {
|
|
578
|
+
query: input.query,
|
|
579
|
+
resultCount: results.length,
|
|
580
|
+
projectId: currentProjectId,
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
return {
|
|
584
|
+
success: true,
|
|
585
|
+
results,
|
|
586
|
+
};
|
|
587
|
+
} catch (error) {
|
|
588
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
589
|
+
logger.error("[ConversationRegistry] Advanced search failed", {
|
|
590
|
+
error: errorMessage,
|
|
591
|
+
query: input.query,
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
return {
|
|
595
|
+
success: false,
|
|
596
|
+
results: [],
|
|
597
|
+
error: errorMessage,
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Trigger an index update for a conversation.
|
|
604
|
+
* Updates are debounced (30 seconds) to avoid excessive I/O.
|
|
605
|
+
*/
|
|
606
|
+
triggerIndexUpdate(conversationId: string): void {
|
|
607
|
+
const currentProjectId = this.resolveProjectId();
|
|
608
|
+
if (!currentProjectId) return;
|
|
609
|
+
|
|
610
|
+
try {
|
|
611
|
+
const indexManager = getIndexManager(this._basePath, currentProjectId);
|
|
612
|
+
indexManager.triggerUpdate(conversationId);
|
|
613
|
+
} catch (error) {
|
|
614
|
+
logger.warn("[ConversationRegistry] Failed to trigger index update", {
|
|
615
|
+
conversationId: conversationId.substring(0, 8),
|
|
616
|
+
error,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Rebuild the search index from scratch.
|
|
623
|
+
* Use sparingly as this scans all conversation files.
|
|
624
|
+
*/
|
|
625
|
+
rebuildSearchIndex(): void {
|
|
626
|
+
const currentProjectId = this.resolveProjectId();
|
|
627
|
+
if (!currentProjectId) return;
|
|
628
|
+
|
|
629
|
+
try {
|
|
630
|
+
const indexManager = getIndexManager(this._basePath, currentProjectId);
|
|
631
|
+
indexManager.rebuildIndex();
|
|
632
|
+
} catch (error) {
|
|
633
|
+
logger.error("[ConversationRegistry] Failed to rebuild search index", { error });
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Get the conversations directory for the current project.
|
|
639
|
+
*/
|
|
640
|
+
getConversationsDir(): string | null {
|
|
641
|
+
const currentProjectId = this.resolveProjectId();
|
|
642
|
+
if (!currentProjectId) return null;
|
|
643
|
+
return join(this._basePath, currentProjectId, "conversations");
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* List conversation IDs from disk for current project.
|
|
648
|
+
*/
|
|
649
|
+
listConversationIdsFromDisk(): string[] {
|
|
650
|
+
const currentProjectId = this.resolveProjectId();
|
|
651
|
+
if (!currentProjectId) return [];
|
|
652
|
+
return listConversationIdsFromDiskForProject(this._basePath, currentProjectId);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* List all project IDs from disk.
|
|
657
|
+
*/
|
|
658
|
+
listProjectIdsFromDisk(): string[] {
|
|
659
|
+
return listProjectIdsFromDisk(this._basePath);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* List conversation IDs for a specific project.
|
|
664
|
+
*/
|
|
665
|
+
listConversationIdsFromDiskForProject(projectId: string): string[] {
|
|
666
|
+
return listConversationIdsFromDiskForProject(this._basePath, projectId);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Check if a pubkey belongs to an agent (across all initialized projects).
|
|
671
|
+
*/
|
|
672
|
+
isAgentPubkey(pubkey: string): boolean {
|
|
673
|
+
return this._allAgentPubkeys.has(pubkey);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Read lightweight metadata without loading full store.
|
|
678
|
+
*/
|
|
679
|
+
readLightweightMetadata(conversationId: string): ReturnType<typeof readLightweightMetadata> {
|
|
680
|
+
const currentProjectId = this.resolveProjectId();
|
|
681
|
+
if (!currentProjectId) return null;
|
|
682
|
+
return readLightweightMetadata(this._basePath, currentProjectId, conversationId);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Read messages from disk without caching.
|
|
687
|
+
*/
|
|
688
|
+
readMessagesFromDisk(conversationId: string): ReturnType<typeof readMessagesFromDisk> {
|
|
689
|
+
const currentProjectId = this.resolveProjectId();
|
|
690
|
+
if (!currentProjectId) return null;
|
|
691
|
+
return readMessagesFromDisk(this._basePath, currentProjectId, conversationId);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Read conversation preview data.
|
|
696
|
+
*/
|
|
697
|
+
readConversationPreview(conversationId: string, agentPubkey: string): ReturnType<typeof readConversationPreviewForProject> {
|
|
698
|
+
const currentProjectId = this.resolveProjectId();
|
|
699
|
+
if (!currentProjectId) return null;
|
|
700
|
+
return readConversationPreviewForProject(this._basePath, conversationId, agentPubkey, currentProjectId);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Read conversation preview for a specific project.
|
|
705
|
+
*/
|
|
706
|
+
readConversationPreviewForProject(
|
|
707
|
+
conversationId: string,
|
|
708
|
+
agentPubkey: string,
|
|
709
|
+
projectId: string
|
|
710
|
+
): ReturnType<typeof readConversationPreviewForProject> {
|
|
711
|
+
return readConversationPreviewForProject(this._basePath, conversationId, agentPubkey, projectId);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Reset all state (for testing).
|
|
716
|
+
*/
|
|
717
|
+
reset(): void {
|
|
718
|
+
this.stores.clear();
|
|
719
|
+
this.eventCache.clear();
|
|
720
|
+
this._basePath = join(getTenexBasePath(), "projects");
|
|
721
|
+
this._projectConfigs.clear();
|
|
722
|
+
this._allAgentPubkeys.clear();
|
|
723
|
+
this._legacyProjectId = null;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Export singleton instance
|
|
728
|
+
export const conversationRegistry = new ConversationRegistryImpl();
|