@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,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation Search Module
|
|
3
|
+
*
|
|
4
|
+
* Provides full-text, keyword, and semantic search capabilities for conversations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Types
|
|
8
|
+
export type {
|
|
9
|
+
AdvancedSearchResult,
|
|
10
|
+
ConversationIndex,
|
|
11
|
+
ConversationIndexEntry,
|
|
12
|
+
IndexMetadata,
|
|
13
|
+
MessageIndexEntry,
|
|
14
|
+
MessageMatch,
|
|
15
|
+
SearchFilters,
|
|
16
|
+
SearchQuery,
|
|
17
|
+
SearchResult,
|
|
18
|
+
} from "./types";
|
|
19
|
+
|
|
20
|
+
// Query Parser
|
|
21
|
+
export {
|
|
22
|
+
getEffectiveSinceTimestamp,
|
|
23
|
+
parseQuery,
|
|
24
|
+
parseTimestamp,
|
|
25
|
+
type RawSearchInput,
|
|
26
|
+
} from "./QueryParser";
|
|
27
|
+
|
|
28
|
+
// Snippet Extractor
|
|
29
|
+
export {
|
|
30
|
+
extractAllSnippets,
|
|
31
|
+
extractSnippet,
|
|
32
|
+
type SnippetResult,
|
|
33
|
+
} from "./SnippetExtractor";
|
|
34
|
+
|
|
35
|
+
// Search Engine
|
|
36
|
+
export {
|
|
37
|
+
search,
|
|
38
|
+
searchByTitleOnly,
|
|
39
|
+
} from "./SearchEngine";
|
|
40
|
+
|
|
41
|
+
// Index Manager
|
|
42
|
+
export {
|
|
43
|
+
clearIndexManagerInstances,
|
|
44
|
+
ConversationIndexManager,
|
|
45
|
+
getIndexManager,
|
|
46
|
+
} from "./ConversationIndexManager";
|
|
47
|
+
|
|
48
|
+
// Embeddings and Semantic Search
|
|
49
|
+
export * from "./embeddings";
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the conversation search module.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Search filters for narrowing down results.
|
|
7
|
+
* Note: 'after' in input is a pure alias for 'since' - only 'since' is stored after parsing.
|
|
8
|
+
*/
|
|
9
|
+
export interface SearchFilters {
|
|
10
|
+
/** Filter by agent slugs or pubkeys - conversation must include at least one */
|
|
11
|
+
agents?: string[];
|
|
12
|
+
/** Return conversations with activity since this timestamp (Unix seconds) */
|
|
13
|
+
since?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Parsed and validated search query.
|
|
18
|
+
*/
|
|
19
|
+
export interface SearchQuery {
|
|
20
|
+
/** The text to search for in message content */
|
|
21
|
+
text: string;
|
|
22
|
+
/** Optional filters to narrow results */
|
|
23
|
+
filters: SearchFilters;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A single message match with snippet context.
|
|
28
|
+
*/
|
|
29
|
+
export interface MessageMatch {
|
|
30
|
+
/** The ID of the matching message */
|
|
31
|
+
messageId: string;
|
|
32
|
+
/** Snippet showing the match with 50-75 char context */
|
|
33
|
+
snippet: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A conversation search result.
|
|
38
|
+
*/
|
|
39
|
+
export interface SearchResult {
|
|
40
|
+
/** The conversation ID */
|
|
41
|
+
conversationId: string;
|
|
42
|
+
/** The conversation title */
|
|
43
|
+
title?: string;
|
|
44
|
+
/** Total number of messages in the conversation */
|
|
45
|
+
messageCount: number;
|
|
46
|
+
/** When the conversation was created (Unix timestamp in seconds) */
|
|
47
|
+
createdAt?: number;
|
|
48
|
+
/** When the last activity occurred (Unix timestamp in seconds) */
|
|
49
|
+
lastActivity?: number;
|
|
50
|
+
/** Array of matching messages with snippets */
|
|
51
|
+
matches: MessageMatch[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Index entry for a single message.
|
|
56
|
+
*/
|
|
57
|
+
export interface MessageIndexEntry {
|
|
58
|
+
/** Unique message identifier (index within conversation) */
|
|
59
|
+
messageId: string;
|
|
60
|
+
/** Full message text content for searching */
|
|
61
|
+
content: string;
|
|
62
|
+
/** Message timestamp (Unix seconds) */
|
|
63
|
+
timestamp?: number;
|
|
64
|
+
/** Sender identifier (pubkey or agent slug) */
|
|
65
|
+
from?: string;
|
|
66
|
+
/** Target recipient identifier */
|
|
67
|
+
to?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Index entry for a conversation.
|
|
72
|
+
*/
|
|
73
|
+
export interface ConversationIndexEntry {
|
|
74
|
+
/** The conversation ID */
|
|
75
|
+
conversationId: string;
|
|
76
|
+
/** Project slug/ID this conversation belongs to */
|
|
77
|
+
slug: string;
|
|
78
|
+
/** Conversation title */
|
|
79
|
+
title?: string;
|
|
80
|
+
/** Total message count */
|
|
81
|
+
messageCount: number;
|
|
82
|
+
/** Last message timestamp (Unix seconds) */
|
|
83
|
+
lastMessageAt?: number;
|
|
84
|
+
/** List of agent pubkeys that participated */
|
|
85
|
+
agents: string[];
|
|
86
|
+
/** Indexed messages */
|
|
87
|
+
messages: MessageIndexEntry[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The full search index structure.
|
|
92
|
+
*/
|
|
93
|
+
export interface ConversationIndex {
|
|
94
|
+
/** Index format version for migration compatibility */
|
|
95
|
+
version: string;
|
|
96
|
+
/** When the index was last updated (ISO 8601) */
|
|
97
|
+
lastUpdated: string;
|
|
98
|
+
/** All indexed conversations */
|
|
99
|
+
conversations: ConversationIndexEntry[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Index file metadata for staleness checks.
|
|
104
|
+
*/
|
|
105
|
+
export interface IndexMetadata {
|
|
106
|
+
version: string;
|
|
107
|
+
lastUpdated: string;
|
|
108
|
+
conversationCount: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Result of an advanced search operation.
|
|
113
|
+
* Includes explicit error information when applicable.
|
|
114
|
+
*/
|
|
115
|
+
export interface AdvancedSearchResult {
|
|
116
|
+
/** Whether the search completed successfully */
|
|
117
|
+
success: boolean;
|
|
118
|
+
/** Search results (empty array if error) */
|
|
119
|
+
results: SearchResult[];
|
|
120
|
+
/** Error message if success is false */
|
|
121
|
+
error?: string;
|
|
122
|
+
/** Whether fallback to title-only search was used */
|
|
123
|
+
usedFallback?: boolean;
|
|
124
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { ensureDirectory, fileExists } from "@/lib/fs";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* CategoryTally represents the stored format: category name -> usage count
|
|
7
|
+
* @example { "authentication": 15, "storage": 8, "testing": 12 }
|
|
8
|
+
*/
|
|
9
|
+
export type CategoryTally = Record<string, number>;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* CategoryManager - Manages conversation categories with usage tracking
|
|
13
|
+
*
|
|
14
|
+
* ## Responsibility
|
|
15
|
+
* Maintains a tally of conversation categories stored in a local JSON file.
|
|
16
|
+
* Categories track usage counts to identify frequently used categorizations.
|
|
17
|
+
*
|
|
18
|
+
* ## Storage Location
|
|
19
|
+
* Categories are stored in `data/conversation-categories.json` relative to the
|
|
20
|
+
* project root. The data directory is created automatically if it doesn't exist.
|
|
21
|
+
*
|
|
22
|
+
* ## Category Format Requirements
|
|
23
|
+
* - Lowercase (e.g., "authentication" not "Authentication")
|
|
24
|
+
* - Singular nouns (e.g., "authentication" not "auth")
|
|
25
|
+
* - No special characters or spaces (e.g., "error-handling" not "error handling")
|
|
26
|
+
*
|
|
27
|
+
* ## Thread Safety
|
|
28
|
+
* All file operations use async/await to ensure proper sequencing.
|
|
29
|
+
* The class maintains no in-memory cache to avoid stale data issues.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const manager = new CategoryManager('/path/to/project');
|
|
33
|
+
* await manager.initialize();
|
|
34
|
+
*
|
|
35
|
+
* // Get existing categories
|
|
36
|
+
* const categories = await manager.getCategories();
|
|
37
|
+
* // Returns: ["authentication", "storage", "testing"]
|
|
38
|
+
*
|
|
39
|
+
* // Update with new categories
|
|
40
|
+
* await manager.updateCategories(["authentication", "database"]);
|
|
41
|
+
* // Increments "authentication" count, adds "database" with count 1
|
|
42
|
+
*/
|
|
43
|
+
export class CategoryManager {
|
|
44
|
+
private dataDir: string;
|
|
45
|
+
private categoriesFilePath: string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates a new CategoryManager instance
|
|
49
|
+
* @param projectRoot - The root directory of the project
|
|
50
|
+
*/
|
|
51
|
+
constructor(projectRoot: string) {
|
|
52
|
+
this.dataDir = path.join(projectRoot, "data");
|
|
53
|
+
this.categoriesFilePath = path.join(this.dataDir, "conversation-categories.json");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Initialize the CategoryManager by ensuring the data directory exists
|
|
58
|
+
*/
|
|
59
|
+
async initialize(): Promise<void> {
|
|
60
|
+
await ensureDirectory(this.dataDir);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Load the category tally from disk
|
|
65
|
+
* @returns The current category tally, or empty object if file doesn't exist
|
|
66
|
+
*/
|
|
67
|
+
private async loadTally(): Promise<CategoryTally> {
|
|
68
|
+
try {
|
|
69
|
+
if (!(await fileExists(this.categoriesFilePath))) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
const content = await fs.readFile(this.categoriesFilePath, "utf-8");
|
|
73
|
+
return JSON.parse(content) as CategoryTally;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
// If file is corrupted or unreadable, start fresh
|
|
76
|
+
console.error("Failed to load category tally, starting fresh:", error);
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Save the category tally to disk
|
|
83
|
+
* @param tally - The category tally to save
|
|
84
|
+
*/
|
|
85
|
+
private async saveTally(tally: CategoryTally): Promise<void> {
|
|
86
|
+
await ensureDirectory(this.dataDir);
|
|
87
|
+
await fs.writeFile(this.categoriesFilePath, JSON.stringify(tally, null, 2), "utf-8");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get all existing categories as an array
|
|
92
|
+
* Categories are sorted by usage count (highest first)
|
|
93
|
+
* @returns Array of category names
|
|
94
|
+
*/
|
|
95
|
+
async getCategories(): Promise<string[]> {
|
|
96
|
+
const tally = await this.loadTally();
|
|
97
|
+
// Sort by usage count (descending) to prioritize frequently used categories
|
|
98
|
+
return Object.entries(tally)
|
|
99
|
+
.sort(([, countA], [, countB]) => countB - countA)
|
|
100
|
+
.map(([category]) => category);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get the full category tally including usage counts
|
|
105
|
+
* @returns The category tally object
|
|
106
|
+
*/
|
|
107
|
+
async getCategoryTally(): Promise<CategoryTally> {
|
|
108
|
+
return this.loadTally();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Update categories by adding new ones and incrementing counts for existing ones
|
|
113
|
+
*
|
|
114
|
+
* @param newCategories - Array of category names to add/increment
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* // If tally is { "authentication": 5, "storage": 2 }
|
|
118
|
+
* await manager.updateCategories(["authentication", "database"]);
|
|
119
|
+
* // Tally becomes { "authentication": 6, "storage": 2, "database": 1 }
|
|
120
|
+
*/
|
|
121
|
+
async updateCategories(newCategories: string[]): Promise<void> {
|
|
122
|
+
if (newCategories.length === 0) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const tally = await this.loadTally();
|
|
127
|
+
|
|
128
|
+
for (const category of newCategories) {
|
|
129
|
+
// Normalize: lowercase, trim whitespace
|
|
130
|
+
const normalizedCategory = category.toLowerCase().trim();
|
|
131
|
+
|
|
132
|
+
if (!normalizedCategory) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Increment existing or initialize to 1
|
|
137
|
+
tally[normalizedCategory] = (tally[normalizedCategory] || 0) + 1;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await this.saveTally(tally);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Remove a category from the tally
|
|
145
|
+
* @param category - The category to remove
|
|
146
|
+
* @returns true if the category was removed, false if it didn't exist
|
|
147
|
+
*/
|
|
148
|
+
async removeCategory(category: string): Promise<boolean> {
|
|
149
|
+
const tally = await this.loadTally();
|
|
150
|
+
const normalizedCategory = category.toLowerCase().trim();
|
|
151
|
+
|
|
152
|
+
if (!(normalizedCategory in tally)) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
delete tally[normalizedCategory];
|
|
157
|
+
await this.saveTally(tally);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { ConversationStore } from "../ConversationStore";
|
|
2
|
+
import type { ConversationMetadata } from "../types";
|
|
3
|
+
import { AgentEventDecoder } from "@/nostr/AgentEventDecoder";
|
|
4
|
+
import { shortenConversationId } from "@/utils/conversation-id";
|
|
5
|
+
import { getNDK } from "@/nostr/ndkClient";
|
|
6
|
+
import { getProjectContext } from "@/services/projects";
|
|
7
|
+
import { logger } from "@/utils/logger";
|
|
8
|
+
import { buildDelegationChain } from "@/utils/delegation-chain";
|
|
9
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
10
|
+
import { NDKArticle } from "@nostr-dev-kit/ndk";
|
|
11
|
+
import { trace } from "@opentelemetry/api";
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
import { TagExtractor } from "@/nostr/TagExtractor";
|
|
14
|
+
import { formatAnyError } from "@/lib/error-formatter";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fetch a kind 30023 (NDKArticle) from an a-tag reference.
|
|
18
|
+
* @param aTagValue - The a-tag value in format "30023:pubkey:d-tag"
|
|
19
|
+
* @returns The article metadata or null if not found
|
|
20
|
+
*/
|
|
21
|
+
async function fetchReferencedArticle(
|
|
22
|
+
aTagValue: string
|
|
23
|
+
): Promise<ConversationMetadata["referencedArticle"] | null> {
|
|
24
|
+
try {
|
|
25
|
+
const parts = aTagValue.split(":");
|
|
26
|
+
if (parts.length < 3 || parts[0] !== "30023") {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const [, pubkey, ...dTagParts] = parts;
|
|
31
|
+
const dTag = dTagParts.join(":"); // Handle d-tags that contain colons
|
|
32
|
+
|
|
33
|
+
const ndk = getNDK();
|
|
34
|
+
const filter = {
|
|
35
|
+
kinds: [30023],
|
|
36
|
+
authors: [pubkey],
|
|
37
|
+
"#d": [dTag],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const events = await ndk.fetchEvents(filter);
|
|
41
|
+
if (events.size === 0) {
|
|
42
|
+
logger.debug(chalk.yellow(`Referenced article not found: ${aTagValue}`));
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const event = Array.from(events)[0];
|
|
47
|
+
const article = NDKArticle.from(event);
|
|
48
|
+
|
|
49
|
+
logger.info(chalk.cyan(`📄 Fetched referenced article: "${article.title || dTag}"`));
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
title: article.title || dTag,
|
|
53
|
+
content: article.content || "",
|
|
54
|
+
dTag,
|
|
55
|
+
};
|
|
56
|
+
} catch (error) {
|
|
57
|
+
logger.debug(chalk.yellow(`Failed to fetch referenced article: ${formatAnyError(error)}`));
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Extract and fetch the first kind 30023 article reference from an event's a-tags.
|
|
64
|
+
* @param event - The event to extract article references from
|
|
65
|
+
* @returns The article metadata or null if none found
|
|
66
|
+
*/
|
|
67
|
+
async function extractReferencedArticle(
|
|
68
|
+
event: NDKEvent
|
|
69
|
+
): Promise<ConversationMetadata["referencedArticle"] | null> {
|
|
70
|
+
const aTags = TagExtractor.getATags(event);
|
|
71
|
+
|
|
72
|
+
// Find the first a-tag referencing a kind 30023 (article)
|
|
73
|
+
const articleATag = aTags.find((tag) => tag.startsWith("30023:"));
|
|
74
|
+
if (!articleATag) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return fetchReferencedArticle(articleATag);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ConversationResolutionResult {
|
|
82
|
+
conversation: ConversationStore | undefined;
|
|
83
|
+
isNew?: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* ConversationResolver encapsulates all logic for finding or creating conversations
|
|
88
|
+
* based on incoming Nostr events.
|
|
89
|
+
*/
|
|
90
|
+
export class ConversationResolver {
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve the conversation for an incoming event.
|
|
94
|
+
*/
|
|
95
|
+
async resolveConversationForEvent(event: NDKEvent): Promise<ConversationResolutionResult> {
|
|
96
|
+
const activeSpan = trace.getActiveSpan();
|
|
97
|
+
const replyTarget = AgentEventDecoder.getReplyTarget(event);
|
|
98
|
+
|
|
99
|
+
// If event has an 'e' tag (reply), try to find existing conversation
|
|
100
|
+
if (replyTarget) {
|
|
101
|
+
const conversation = ConversationStore.findByEventId(replyTarget);
|
|
102
|
+
if (conversation) {
|
|
103
|
+
activeSpan?.addEvent("conversation.resolved", {
|
|
104
|
+
"resolution.type": "found_existing",
|
|
105
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
106
|
+
"conversation.message_count": conversation.getAllMessages().length,
|
|
107
|
+
});
|
|
108
|
+
return { conversation };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Has e tag but conversation not found - try orphaned reply handling
|
|
112
|
+
const mentionedPubkeys = AgentEventDecoder.getMentionedPubkeys(event);
|
|
113
|
+
const newConversation = await this.handleOrphanedReply(event, replyTarget, mentionedPubkeys);
|
|
114
|
+
if (newConversation) {
|
|
115
|
+
activeSpan?.addEvent("conversation.resolved", {
|
|
116
|
+
"resolution.type": "created_from_orphan",
|
|
117
|
+
"conversation.id": shortenConversationId(newConversation.id),
|
|
118
|
+
"conversation.message_count": newConversation.getAllMessages().length,
|
|
119
|
+
});
|
|
120
|
+
return { conversation: newConversation, isNew: true };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
activeSpan?.addEvent("conversation.resolution_failed", {
|
|
124
|
+
reason: "conversation_not_found_for_reply_target",
|
|
125
|
+
"reply_target.id": replyTarget,
|
|
126
|
+
});
|
|
127
|
+
return { conversation: undefined };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// No 'e' tag - this is a NEW conversation, create it
|
|
131
|
+
const mentionedPubkeys = AgentEventDecoder.getMentionedPubkeys(event);
|
|
132
|
+
const projectCtx = getProjectContext();
|
|
133
|
+
const isDirectedToAgent = mentionedPubkeys.some((pubkey) =>
|
|
134
|
+
Array.from(projectCtx.agents.values()).some((a) => a.pubkey === pubkey)
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (!isDirectedToAgent) {
|
|
138
|
+
activeSpan?.addEvent("conversation.resolution_failed", {
|
|
139
|
+
reason: "not_directed_to_agent",
|
|
140
|
+
});
|
|
141
|
+
return { conversation: undefined };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const conversation = await ConversationStore.create(event);
|
|
145
|
+
if (conversation) {
|
|
146
|
+
// Check for referenced kind 30023 articles and populate metadata
|
|
147
|
+
const referencedArticle = await extractReferencedArticle(event);
|
|
148
|
+
if (referencedArticle) {
|
|
149
|
+
conversation.updateMetadata({ referencedArticle });
|
|
150
|
+
await conversation.save();
|
|
151
|
+
|
|
152
|
+
activeSpan?.addEvent("referenced_article_loaded", {
|
|
153
|
+
"article.title": referencedArticle.title,
|
|
154
|
+
"article.dTag": referencedArticle.dTag,
|
|
155
|
+
"article.content_length": referencedArticle.content.length,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Build and store delegation chain if this is a delegated conversation
|
|
160
|
+
// For delegation events, the first p-tag is always the intended recipient.
|
|
161
|
+
// getMentionedPubkeys returns p-tags in order, so the first agent pubkey
|
|
162
|
+
// found is the correct target.
|
|
163
|
+
const targetAgentPubkey = mentionedPubkeys.find((pubkey) =>
|
|
164
|
+
Array.from(projectCtx.agents.values()).some((a) => a.pubkey === pubkey)
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
if (targetAgentPubkey) {
|
|
168
|
+
const delegationChain = buildDelegationChain(
|
|
169
|
+
event,
|
|
170
|
+
targetAgentPubkey,
|
|
171
|
+
projectCtx.project.pubkey, // Project owner is the human user
|
|
172
|
+
conversation.id // The conversation being created for the current agent
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (delegationChain && delegationChain.length > 0) {
|
|
176
|
+
conversation.updateMetadata({ delegationChain });
|
|
177
|
+
await conversation.save();
|
|
178
|
+
|
|
179
|
+
activeSpan?.addEvent("delegation_chain_built", {
|
|
180
|
+
"chain.length": delegationChain.length,
|
|
181
|
+
"chain.display": delegationChain.map(c => c.displayName).join(" → "),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
logger.debug("[ConversationResolver] Built delegation chain for new conversation", {
|
|
185
|
+
conversationId: conversation.id.substring(0, 8),
|
|
186
|
+
chainLength: delegationChain.length,
|
|
187
|
+
chain: delegationChain.map(c => c.displayName).join(" → "),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
logger.info(chalk.green(`Created new conversation ${conversation.id.substring(0, 8)} from kind:1 event`));
|
|
193
|
+
activeSpan?.addEvent("conversation.resolved", {
|
|
194
|
+
"resolution.type": "created_new",
|
|
195
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
196
|
+
});
|
|
197
|
+
return { conversation, isNew: true };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
activeSpan?.addEvent("conversation.resolution_failed", {
|
|
201
|
+
reason: "failed_to_create_conversation",
|
|
202
|
+
});
|
|
203
|
+
return { conversation: undefined };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Handle orphaned replies by fetching the thread from the network
|
|
208
|
+
*/
|
|
209
|
+
private async handleOrphanedReply(
|
|
210
|
+
event: NDKEvent,
|
|
211
|
+
replyTargetId: string,
|
|
212
|
+
mentionedPubkeys: string[]
|
|
213
|
+
): Promise<ConversationStore | undefined> {
|
|
214
|
+
if (mentionedPubkeys.length === 0) {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const projectCtx = getProjectContext();
|
|
219
|
+
const isDirectedToAgent = mentionedPubkeys.some((pubkey) =>
|
|
220
|
+
Array.from(projectCtx.agents.values()).some((a) => a.pubkey === pubkey)
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
if (!isDirectedToAgent) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
logger.info(
|
|
228
|
+
chalk.yellow(
|
|
229
|
+
`Fetching conversation thread for orphaned reply, target: ${replyTargetId.substring(0, 8)}`
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
const activeSpan = trace.getActiveSpan();
|
|
234
|
+
activeSpan?.addEvent("conversation.fetching_orphaned_thread", {
|
|
235
|
+
reply_target_id: replyTargetId,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const ndk = getNDK();
|
|
239
|
+
|
|
240
|
+
// Fetch the reply target event and any events that also reply to it
|
|
241
|
+
const events = await ndk.fetchEvents([
|
|
242
|
+
{ ids: [replyTargetId] },
|
|
243
|
+
{ "#e": [replyTargetId] },
|
|
244
|
+
]);
|
|
245
|
+
|
|
246
|
+
const eventsArray = Array.from(events);
|
|
247
|
+
const rootEvent = eventsArray.find((e) => e.id === replyTargetId);
|
|
248
|
+
|
|
249
|
+
if (!rootEvent) {
|
|
250
|
+
logger.warn(chalk.yellow(`Could not fetch target event ${replyTargetId.substring(0, 8)} from network`));
|
|
251
|
+
activeSpan?.addEvent("conversation.fetch_failed", {
|
|
252
|
+
reason: "target_event_not_found",
|
|
253
|
+
reply_target_id: replyTargetId,
|
|
254
|
+
});
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const replies = eventsArray.filter((e) => e.id !== replyTargetId);
|
|
259
|
+
|
|
260
|
+
logger.info(chalk.green(`Fetched target event and ${replies.length} replies`));
|
|
261
|
+
activeSpan?.addEvent("conversation.thread_fetched", {
|
|
262
|
+
"fetched.reply_count": replies.length,
|
|
263
|
+
"fetched.total_events": eventsArray.length,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const conversation = await ConversationStore.create(rootEvent);
|
|
267
|
+
if (!conversation) {
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Check for referenced kind 30023 articles in the root event and populate metadata
|
|
272
|
+
const referencedArticle = await extractReferencedArticle(rootEvent);
|
|
273
|
+
if (referencedArticle) {
|
|
274
|
+
conversation.updateMetadata({ referencedArticle });
|
|
275
|
+
await conversation.save();
|
|
276
|
+
|
|
277
|
+
activeSpan?.addEvent("referenced_article_loaded", {
|
|
278
|
+
"article.title": referencedArticle.title,
|
|
279
|
+
"article.dTag": referencedArticle.dTag,
|
|
280
|
+
"article.content_length": referencedArticle.content.length,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
replies.sort((a, b) => (a.created_at || 0) - (b.created_at || 0));
|
|
285
|
+
|
|
286
|
+
for (const reply of replies) {
|
|
287
|
+
await ConversationStore.addEvent(conversation.id, reply);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (event.id !== rootEvent.id && !replies.some((r) => r.id === event.id)) {
|
|
291
|
+
await ConversationStore.addEvent(conversation.id, event);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return conversation;
|
|
295
|
+
}
|
|
296
|
+
}
|