@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,937 @@
|
|
|
1
|
+
import type { AgentExecutor } from "@/agents/execution/AgentExecutor";
|
|
2
|
+
import { createExecutionContext } from "@/agents/execution/ExecutionContextFactory";
|
|
3
|
+
import type { AgentInstance } from "@/agents/types";
|
|
4
|
+
import { ConversationStore } from "@/conversations/ConversationStore";
|
|
5
|
+
import { ConversationResolver } from "@/conversations/services/ConversationResolver";
|
|
6
|
+
import { ConversationSummarizer } from "@/conversations/services/ConversationSummarizer";
|
|
7
|
+
import { metadataDebounceManager } from "@/conversations/services/MetadataDebounceManager";
|
|
8
|
+
import type { DelegationMarker } from "@/conversations/types";
|
|
9
|
+
import { formatAnyError } from "@/lib/error-formatter";
|
|
10
|
+
import { shortenConversationId } from "@/utils/conversation-id";
|
|
11
|
+
import { AgentEventDecoder } from "@/nostr/AgentEventDecoder";
|
|
12
|
+
import { config } from "@/services/ConfigService";
|
|
13
|
+
import { PROVIDER_IDS } from "@/llm/providers/provider-ids";
|
|
14
|
+
import { llmOpsRegistry, INJECTION_ABORT_REASON } from "@/services/LLMOperationsRegistry";
|
|
15
|
+
import { getProjectContext, type ProjectContext } from "@/services/projects";
|
|
16
|
+
import { RALRegistry } from "@/services/ral";
|
|
17
|
+
import type { RALRegistryEntry } from "@/services/ral/types";
|
|
18
|
+
import { logger } from "@/utils/logger";
|
|
19
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
20
|
+
import { ROOT_CONTEXT, SpanStatusCode, context as otelContext, trace } from "@opentelemetry/api";
|
|
21
|
+
import { AgentRouter } from "@/services/dispatch/AgentRouter";
|
|
22
|
+
import { handleDelegationCompletion } from "@/services/dispatch/DelegationCompletionHandler";
|
|
23
|
+
|
|
24
|
+
const tracer = trace.getTracer("tenex.dispatch");
|
|
25
|
+
// Coalesce back-to-back delegation completions so we resume once with a stable snapshot.
|
|
26
|
+
const DELEGATION_COMPLETION_DEBOUNCE_MS = 2500;
|
|
27
|
+
const getSafeContext = (): ReturnType<typeof otelContext.active> => {
|
|
28
|
+
const activeContext = otelContext.active();
|
|
29
|
+
// Defensive fallback for test mocks or non-standard context managers.
|
|
30
|
+
return typeof (activeContext as { getValue?: unknown }).getValue === "function"
|
|
31
|
+
? activeContext
|
|
32
|
+
: ROOT_CONTEXT;
|
|
33
|
+
};
|
|
34
|
+
const getSafeActiveSpan = (): ReturnType<typeof trace.getActiveSpan> => {
|
|
35
|
+
try {
|
|
36
|
+
return trace.getActiveSpan();
|
|
37
|
+
} catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
interface DispatchContext {
|
|
43
|
+
agentExecutor: AgentExecutor;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface DelegationTarget {
|
|
47
|
+
agent: AgentInstance;
|
|
48
|
+
conversationId: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class AgentDispatchService {
|
|
52
|
+
private static instance: AgentDispatchService;
|
|
53
|
+
private readonly delegationDebounceState = new Map<
|
|
54
|
+
string,
|
|
55
|
+
{ timeout: ReturnType<typeof setTimeout>; promise: Promise<void>; resolve: () => void }
|
|
56
|
+
>();
|
|
57
|
+
private readonly delegationDebounceSequence = new Map<string, number>();
|
|
58
|
+
|
|
59
|
+
private constructor() {}
|
|
60
|
+
|
|
61
|
+
static getInstance(): AgentDispatchService {
|
|
62
|
+
if (!AgentDispatchService.instance) {
|
|
63
|
+
AgentDispatchService.instance = new AgentDispatchService();
|
|
64
|
+
}
|
|
65
|
+
return AgentDispatchService.instance;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async dispatch(event: NDKEvent, context: DispatchContext): Promise<void> {
|
|
69
|
+
const span = tracer.startSpan(
|
|
70
|
+
"tenex.dispatch.chat_message",
|
|
71
|
+
{
|
|
72
|
+
attributes: {
|
|
73
|
+
"event.id": event.id ?? "",
|
|
74
|
+
"event.pubkey": event.pubkey ?? "",
|
|
75
|
+
"event.kind": event.kind ?? 0,
|
|
76
|
+
"event.content_length": event.content?.length ?? 0,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
getSafeContext()
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
await this.handleChatMessage(event, context, span);
|
|
84
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
85
|
+
} catch (error) {
|
|
86
|
+
span.recordException(error as Error);
|
|
87
|
+
span.setStatus({
|
|
88
|
+
code: SpanStatusCode.ERROR,
|
|
89
|
+
message: (error as Error).message,
|
|
90
|
+
});
|
|
91
|
+
logger.error("Failed to route reply", {
|
|
92
|
+
error: formatAnyError(error),
|
|
93
|
+
eventId: event.id,
|
|
94
|
+
});
|
|
95
|
+
} finally {
|
|
96
|
+
span.end();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private async handleChatMessage(
|
|
101
|
+
event: NDKEvent,
|
|
102
|
+
{ agentExecutor }: DispatchContext,
|
|
103
|
+
span: ReturnType<typeof tracer.startSpan>
|
|
104
|
+
): Promise<void> {
|
|
105
|
+
const projectCtx = getProjectContext();
|
|
106
|
+
|
|
107
|
+
const isDirectedToSystem = AgentEventDecoder.isDirectedToSystem(event, projectCtx.agents);
|
|
108
|
+
const isFromAgent = AgentEventDecoder.isEventFromAgent(event, projectCtx.agents);
|
|
109
|
+
|
|
110
|
+
span.setAttributes({
|
|
111
|
+
"routing.is_directed_to_system": isDirectedToSystem,
|
|
112
|
+
"routing.is_from_agent": isFromAgent,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
getSafeActiveSpan()?.addEvent("reply.message_received", {
|
|
116
|
+
"event.id": event.id ?? "",
|
|
117
|
+
"event.pubkey": event.pubkey?.substring(0, 8) ?? "",
|
|
118
|
+
"message.preview": event.content.substring(0, 100),
|
|
119
|
+
"routing.is_directed_to_system": isDirectedToSystem,
|
|
120
|
+
"routing.is_from_agent": isFromAgent,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
span.addEvent("dispatch.message_received", {
|
|
124
|
+
"routing.is_directed_to_system": isDirectedToSystem,
|
|
125
|
+
"routing.is_from_agent": isFromAgent,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (!isDirectedToSystem && isFromAgent) {
|
|
129
|
+
getSafeActiveSpan()?.addEvent("reply.agent_event_not_directed", {
|
|
130
|
+
"event.id": event.id ?? "",
|
|
131
|
+
});
|
|
132
|
+
span.addEvent("dispatch.agent_event_not_directed");
|
|
133
|
+
|
|
134
|
+
const resolver = new ConversationResolver();
|
|
135
|
+
const result = await resolver.resolveConversationForEvent(event);
|
|
136
|
+
|
|
137
|
+
if (result.conversation) {
|
|
138
|
+
await ConversationStore.addEvent(result.conversation.id, event);
|
|
139
|
+
getSafeActiveSpan()?.addEvent("reply.added_to_history", {
|
|
140
|
+
"conversation.id": shortenConversationId(result.conversation.id),
|
|
141
|
+
});
|
|
142
|
+
span.addEvent("dispatch.agent_event_added_to_history", {
|
|
143
|
+
"conversation.id": shortenConversationId(result.conversation.id),
|
|
144
|
+
});
|
|
145
|
+
} else {
|
|
146
|
+
getSafeActiveSpan()?.addEvent("reply.no_conversation_found", {
|
|
147
|
+
"event.id": event.id ?? "",
|
|
148
|
+
});
|
|
149
|
+
span.addEvent("dispatch.agent_event_no_conversation");
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
await this.handleReplyLogic(event, agentExecutor, projectCtx, span);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private async handleReplyLogic(
|
|
158
|
+
event: NDKEvent,
|
|
159
|
+
agentExecutor: AgentExecutor,
|
|
160
|
+
projectCtx: ProjectContext,
|
|
161
|
+
span: ReturnType<typeof tracer.startSpan>
|
|
162
|
+
): Promise<void> {
|
|
163
|
+
const delegationResult = await handleDelegationCompletion(event);
|
|
164
|
+
const delegationTarget = AgentRouter.resolveDelegationTarget(delegationResult, projectCtx);
|
|
165
|
+
|
|
166
|
+
if (delegationTarget) {
|
|
167
|
+
span.addEvent("dispatch.delegation_completion_routed", {
|
|
168
|
+
"delegation.agent_slug": delegationTarget.agent.slug,
|
|
169
|
+
"delegation.conversation_id": shortenConversationId(delegationTarget.conversationId),
|
|
170
|
+
});
|
|
171
|
+
await this.handleDelegationResponse(event, delegationTarget, agentExecutor, projectCtx, span);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (AgentEventDecoder.isDelegationCompletion(event)) {
|
|
176
|
+
const activeSpan = getSafeActiveSpan();
|
|
177
|
+
activeSpan?.addEvent("reply.completion_dropped_no_waiting_ral", {
|
|
178
|
+
"event.id": event.id ?? "",
|
|
179
|
+
"event.pubkey": event.pubkey.substring(0, 8),
|
|
180
|
+
});
|
|
181
|
+
activeSpan?.setStatus({
|
|
182
|
+
code: SpanStatusCode.ERROR,
|
|
183
|
+
message: "Delegation completion dropped: no waiting RAL found. This indicates a delegation registration bug.",
|
|
184
|
+
});
|
|
185
|
+
logger.error("[reply] Delegation completion dropped - no waiting RAL", {
|
|
186
|
+
eventId: event.id,
|
|
187
|
+
eventPubkey: event.pubkey.substring(0, 8),
|
|
188
|
+
});
|
|
189
|
+
span.addEvent("dispatch.delegation_completion_dropped");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const conversationResolver = new ConversationResolver();
|
|
194
|
+
const { conversation, isNew } = await conversationResolver.resolveConversationForEvent(event);
|
|
195
|
+
|
|
196
|
+
if (!conversation) {
|
|
197
|
+
logger.error("No conversation found or created for event", {
|
|
198
|
+
eventId: event.id,
|
|
199
|
+
replyTarget: AgentEventDecoder.getReplyTarget(event),
|
|
200
|
+
});
|
|
201
|
+
span.addEvent("dispatch.conversation_missing", {
|
|
202
|
+
"event.id": event.id ?? "",
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
span.setAttributes({
|
|
208
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
209
|
+
"conversation.is_new": isNew,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (!isNew && event.id && conversation.hasEventId(event.id)) {
|
|
213
|
+
getSafeActiveSpan()?.addEvent("reply.skipped_duplicate_event", {
|
|
214
|
+
"event.id": event.id,
|
|
215
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
216
|
+
});
|
|
217
|
+
span.addEvent("dispatch.duplicate_event_skipped", {
|
|
218
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
if (!AgentEventDecoder.isAgentInternalMessage(event)) {
|
|
222
|
+
await ConversationStore.addEvent(conversation.id, event);
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!isNew && !AgentEventDecoder.isAgentInternalMessage(event)) {
|
|
228
|
+
await ConversationStore.addEvent(conversation.id, event);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (isNew && !AgentEventDecoder.isAgentInternalMessage(event)) {
|
|
232
|
+
metadataDebounceManager.markFirstPublishDone(conversation.id);
|
|
233
|
+
|
|
234
|
+
const summarizer = new ConversationSummarizer(projectCtx);
|
|
235
|
+
summarizer.summarizeAndPublish(conversation).catch((error) => {
|
|
236
|
+
logger.error("Failed to generate initial metadata for new conversation", {
|
|
237
|
+
conversationId: conversation.id,
|
|
238
|
+
error: formatAnyError(error),
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
getSafeActiveSpan()?.addEvent("reply.initial_metadata_scheduled", {
|
|
242
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
243
|
+
});
|
|
244
|
+
span.addEvent("dispatch.initial_metadata_scheduled", {
|
|
245
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const whitelistedPubkeys = config.getConfig().whitelistedPubkeys ?? [];
|
|
250
|
+
const whitelist = new Set(whitelistedPubkeys);
|
|
251
|
+
if (whitelist.has(event.pubkey)) {
|
|
252
|
+
const { unblocked } = AgentRouter.unblockAgent(event, conversation, projectCtx, whitelist);
|
|
253
|
+
if (unblocked) {
|
|
254
|
+
getSafeActiveSpan()?.addEvent("reply.agent_unblocked_by_whitelist", {
|
|
255
|
+
"event.pubkey": event.pubkey.substring(0, 8),
|
|
256
|
+
});
|
|
257
|
+
span.addEvent("dispatch.agent_unblocked", {
|
|
258
|
+
"event.pubkey": event.pubkey.substring(0, 8),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
getSafeActiveSpan()?.addEvent("reply.before_agent_routing");
|
|
264
|
+
const targetAgents = AgentRouter.resolveTargetAgents(event, projectCtx, conversation);
|
|
265
|
+
|
|
266
|
+
const activeSpan = getSafeActiveSpan();
|
|
267
|
+
if (activeSpan) {
|
|
268
|
+
const mentionedPubkeys = AgentEventDecoder.getMentionedPubkeys(event);
|
|
269
|
+
activeSpan.addEvent("agent_routing", {
|
|
270
|
+
"routing.mentioned_pubkeys_count": mentionedPubkeys.length,
|
|
271
|
+
"routing.resolved_agent_count": targetAgents.length,
|
|
272
|
+
"routing.agent_names": targetAgents.map((a) => a.name).join(", "),
|
|
273
|
+
"routing.agent_roles": targetAgents.map((a) => a.role).join(", "),
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
span.addEvent("dispatch.routing_complete", {
|
|
278
|
+
"routing.resolved_agent_count": targetAgents.length,
|
|
279
|
+
});
|
|
280
|
+
span.setAttributes({
|
|
281
|
+
"routing.target_agent_count": targetAgents.length,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (targetAgents.length === 0) {
|
|
285
|
+
activeSpan?.addEvent("reply.no_target_agents", {
|
|
286
|
+
"event.id": event.id ?? "",
|
|
287
|
+
});
|
|
288
|
+
span.addEvent("dispatch.no_target_agents");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
metadataDebounceManager.onAgentStart(conversation.id);
|
|
293
|
+
|
|
294
|
+
await this.dispatchToAgents({
|
|
295
|
+
targetAgents,
|
|
296
|
+
event,
|
|
297
|
+
conversationId: conversation.id,
|
|
298
|
+
projectCtx,
|
|
299
|
+
agentExecutor,
|
|
300
|
+
parentSpan: span,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
if (!AgentEventDecoder.isAgentInternalMessage(event)) {
|
|
304
|
+
metadataDebounceManager.schedulePublish(
|
|
305
|
+
conversation.id,
|
|
306
|
+
false,
|
|
307
|
+
async () => {
|
|
308
|
+
const summarizer = new ConversationSummarizer(projectCtx);
|
|
309
|
+
await summarizer.summarizeAndPublish(conversation);
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
getSafeActiveSpan()?.addEvent("reply.summarization_scheduled", {
|
|
313
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
314
|
+
"debounced": true,
|
|
315
|
+
});
|
|
316
|
+
span.addEvent("dispatch.summarization_scheduled", {
|
|
317
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private async handleDelegationResponse(
|
|
323
|
+
event: NDKEvent,
|
|
324
|
+
delegationTarget: DelegationTarget,
|
|
325
|
+
agentExecutor: AgentExecutor,
|
|
326
|
+
projectCtx: ProjectContext,
|
|
327
|
+
parentSpan: ReturnType<typeof tracer.startSpan>
|
|
328
|
+
): Promise<void> {
|
|
329
|
+
const span = tracer.startSpan(
|
|
330
|
+
"tenex.dispatch.delegation_response",
|
|
331
|
+
{
|
|
332
|
+
attributes: {
|
|
333
|
+
"delegation.agent_slug": delegationTarget.agent.slug,
|
|
334
|
+
"delegation.conversation_id": shortenConversationId(delegationTarget.conversationId),
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
trace.setSpan(getSafeContext(), parentSpan)
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
// Check if this agent is in cooldown for this conversation
|
|
342
|
+
// Non-null assertion: delegationTarget.conversationId and dTag are guaranteed to be defined (checked in resolveDelegationTarget)
|
|
343
|
+
const isInCooldown = await this.checkAndBlockIfCooldown(
|
|
344
|
+
projectCtx.project.dTag!,
|
|
345
|
+
delegationTarget.conversationId!,
|
|
346
|
+
delegationTarget.agent.pubkey,
|
|
347
|
+
delegationTarget.agent.slug,
|
|
348
|
+
span,
|
|
349
|
+
"delegation_completion"
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
if (isInCooldown) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const ralRegistry = RALRegistry.getInstance();
|
|
357
|
+
const activeRal = ralRegistry.getState(
|
|
358
|
+
delegationTarget.agent.pubkey,
|
|
359
|
+
delegationTarget.conversationId
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
// NOTE: We intentionally do NOT abort streaming executions when delegation completes.
|
|
363
|
+
// The delegator might be mid-stream (waiting for LLM response after a tool call).
|
|
364
|
+
// Aborting would kill the stream before it can finish naturally.
|
|
365
|
+
// Instead, we let the debounce run, then either:
|
|
366
|
+
// - Resume the finished RAL with delegation results
|
|
367
|
+
// - Queue results for an active stream to pick up via prepareStep
|
|
368
|
+
// See trace-detective report on executor.result_undefined_error for details.
|
|
369
|
+
if (activeRal) {
|
|
370
|
+
span.addEvent("dispatch.delegation_completion_received", {
|
|
371
|
+
"ral.number": activeRal.ralNumber,
|
|
372
|
+
"ral.is_streaming": activeRal.isStreaming,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
const debounceKey = `${delegationTarget.agent.pubkey}:${delegationTarget.conversationId}`;
|
|
376
|
+
const debounceSequence = await this.waitForDelegationDebounce(debounceKey, span);
|
|
377
|
+
if (this.delegationDebounceSequence.get(debounceKey) !== debounceSequence) {
|
|
378
|
+
span.addEvent("dispatch.delegation_debounce_skipped", {
|
|
379
|
+
"debounce.sequence": debounceSequence,
|
|
380
|
+
});
|
|
381
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
this.delegationDebounceSequence.delete(debounceKey);
|
|
385
|
+
|
|
386
|
+
// Check if there's still an active streaming RAL after debounce.
|
|
387
|
+
// If so, just queue the delegation results - the prepareStep callback will pick them up.
|
|
388
|
+
// This prevents starting a second execution while the first is still streaming.
|
|
389
|
+
const currentRal = ralRegistry.getState(
|
|
390
|
+
delegationTarget.agent.pubkey,
|
|
391
|
+
delegationTarget.conversationId
|
|
392
|
+
);
|
|
393
|
+
if (currentRal?.isStreaming) {
|
|
394
|
+
// Insert delegation markers directly into ConversationStore
|
|
395
|
+
// The active stream will pick up markers when it rebuilds messages
|
|
396
|
+
const completedDelegations = ralRegistry.getConversationCompletedDelegations(
|
|
397
|
+
delegationTarget.agent.pubkey,
|
|
398
|
+
delegationTarget.conversationId,
|
|
399
|
+
currentRal.ralNumber
|
|
400
|
+
);
|
|
401
|
+
const pendingDelegations = ralRegistry.getConversationPendingDelegations(
|
|
402
|
+
delegationTarget.agent.pubkey,
|
|
403
|
+
delegationTarget.conversationId,
|
|
404
|
+
currentRal.ralNumber
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
// Update markers in the parent conversation (or create if not found)
|
|
408
|
+
const parentStore = ConversationStore.get(delegationTarget.conversationId);
|
|
409
|
+
if (parentStore && completedDelegations.length > 0) {
|
|
410
|
+
for (const completion of completedDelegations) {
|
|
411
|
+
// Try to update existing pending marker first
|
|
412
|
+
const updated = parentStore.updateDelegationMarker(
|
|
413
|
+
completion.delegationConversationId,
|
|
414
|
+
{
|
|
415
|
+
status: completion.status,
|
|
416
|
+
completedAt: completion.completedAt,
|
|
417
|
+
abortReason: completion.status === "aborted" ? completion.abortReason : undefined,
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
// If no pending marker found, create a new one (backward compatibility)
|
|
422
|
+
if (!updated) {
|
|
423
|
+
const marker: DelegationMarker = {
|
|
424
|
+
delegationConversationId: completion.delegationConversationId,
|
|
425
|
+
recipientPubkey: completion.recipientPubkey,
|
|
426
|
+
parentConversationId: delegationTarget.conversationId,
|
|
427
|
+
completedAt: completion.completedAt,
|
|
428
|
+
status: completion.status,
|
|
429
|
+
abortReason: completion.status === "aborted" ? completion.abortReason : undefined,
|
|
430
|
+
};
|
|
431
|
+
parentStore.addDelegationMarker(
|
|
432
|
+
marker,
|
|
433
|
+
delegationTarget.agent.pubkey,
|
|
434
|
+
currentRal.ralNumber
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
await parentStore.save();
|
|
439
|
+
|
|
440
|
+
// Clear completed delegations after inserting markers
|
|
441
|
+
ralRegistry.clearCompletedDelegations(
|
|
442
|
+
delegationTarget.agent.pubkey,
|
|
443
|
+
delegationTarget.conversationId,
|
|
444
|
+
currentRal.ralNumber
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
span.addEvent("dispatch.delegation_markers_inserted_for_active_stream", {
|
|
449
|
+
"ral.number": currentRal.ralNumber,
|
|
450
|
+
"delegation.completed_count": completedDelegations.length,
|
|
451
|
+
"delegation.pending_count": pendingDelegations.length,
|
|
452
|
+
});
|
|
453
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const resumableRal = ralRegistry.findResumableRAL(
|
|
458
|
+
delegationTarget.agent.pubkey,
|
|
459
|
+
delegationTarget.conversationId
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
let triggeringEventForContext = event;
|
|
463
|
+
|
|
464
|
+
if (resumableRal?.originalTriggeringEventId) {
|
|
465
|
+
const originalEvent = ConversationStore.getCachedEvent(resumableRal.originalTriggeringEventId);
|
|
466
|
+
if (originalEvent) {
|
|
467
|
+
triggeringEventForContext = originalEvent;
|
|
468
|
+
getSafeActiveSpan()?.addEvent("reply.restored_original_trigger_for_delegation", {
|
|
469
|
+
"original.event_id": resumableRal.originalTriggeringEventId,
|
|
470
|
+
"completion.event_id": event.id || "",
|
|
471
|
+
});
|
|
472
|
+
span.addEvent("dispatch.delegation_restored_trigger", {
|
|
473
|
+
"original.event_id": resumableRal.originalTriggeringEventId,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
getSafeActiveSpan()?.addEvent("reply.delegation_routing_to_original", {
|
|
479
|
+
"delegation.agent_slug": delegationTarget.agent.slug,
|
|
480
|
+
"delegation.original_conversation_id": shortenConversationId(delegationTarget.conversationId),
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
const pendingDelegations = ralRegistry.getConversationPendingDelegations(
|
|
484
|
+
delegationTarget.agent.pubkey,
|
|
485
|
+
delegationTarget.conversationId,
|
|
486
|
+
resumableRal?.ralNumber
|
|
487
|
+
);
|
|
488
|
+
const hasPendingDelegations = pendingDelegations.length > 0;
|
|
489
|
+
|
|
490
|
+
// DIAGNOSTIC: Trace the moment hasPendingDelegations is captured for context
|
|
491
|
+
span.addEvent("dispatch.hasPendingDelegations_captured", {
|
|
492
|
+
"hasPendingDelegations": hasPendingDelegations,
|
|
493
|
+
"pendingDelegations.count": pendingDelegations.length,
|
|
494
|
+
"pendingDelegations.ids": pendingDelegations.map(d => d.delegationConversationId).join(","),
|
|
495
|
+
"ral.number": resumableRal?.ralNumber ?? -1,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
span.setAttributes({
|
|
499
|
+
"delegation.pending_count": pendingDelegations.length,
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
const executionContext = await createExecutionContext({
|
|
503
|
+
agent: delegationTarget.agent,
|
|
504
|
+
conversationId: delegationTarget.conversationId,
|
|
505
|
+
projectBasePath: projectCtx.agentRegistry.getBasePath(),
|
|
506
|
+
triggeringEvent: triggeringEventForContext,
|
|
507
|
+
isDelegationCompletion: true,
|
|
508
|
+
hasPendingDelegations,
|
|
509
|
+
mcpManager: projectCtx.mcpManager,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
metadataDebounceManager.onAgentStart(delegationTarget.conversationId);
|
|
513
|
+
|
|
514
|
+
// Execute within span context so agent.execute becomes a child span
|
|
515
|
+
await otelContext.with(trace.setSpan(otelContext.active(), span), async () => {
|
|
516
|
+
await agentExecutor.execute(executionContext);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
metadataDebounceManager.schedulePublish(
|
|
520
|
+
delegationTarget.conversationId,
|
|
521
|
+
false,
|
|
522
|
+
async () => {
|
|
523
|
+
const summarizer = new ConversationSummarizer(projectCtx);
|
|
524
|
+
const originalConversation = ConversationStore.get(delegationTarget.conversationId);
|
|
525
|
+
if (originalConversation) {
|
|
526
|
+
await summarizer.summarizeAndPublish(originalConversation);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
532
|
+
} catch (error) {
|
|
533
|
+
span.recordException(error as Error);
|
|
534
|
+
span.setStatus({
|
|
535
|
+
code: SpanStatusCode.ERROR,
|
|
536
|
+
message: (error as Error).message,
|
|
537
|
+
});
|
|
538
|
+
throw error;
|
|
539
|
+
} finally {
|
|
540
|
+
span.end();
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
private async waitForDelegationDebounce(
|
|
545
|
+
key: string,
|
|
546
|
+
span: ReturnType<typeof tracer.startSpan>
|
|
547
|
+
): Promise<number> {
|
|
548
|
+
const nextSequence = (this.delegationDebounceSequence.get(key) ?? 0) + 1;
|
|
549
|
+
this.delegationDebounceSequence.set(key, nextSequence);
|
|
550
|
+
|
|
551
|
+
let state = this.delegationDebounceState.get(key);
|
|
552
|
+
if (!state) {
|
|
553
|
+
let resolveFn: (() => void) | undefined;
|
|
554
|
+
const promise = new Promise<void>((resolve) => {
|
|
555
|
+
resolveFn = resolve;
|
|
556
|
+
});
|
|
557
|
+
const timeout = setTimeout(() => {
|
|
558
|
+
this.delegationDebounceState.delete(key);
|
|
559
|
+
resolveFn?.();
|
|
560
|
+
}, DELEGATION_COMPLETION_DEBOUNCE_MS);
|
|
561
|
+
state = {
|
|
562
|
+
timeout,
|
|
563
|
+
promise,
|
|
564
|
+
resolve: resolveFn ?? (() => {}),
|
|
565
|
+
};
|
|
566
|
+
this.delegationDebounceState.set(key, state);
|
|
567
|
+
} else {
|
|
568
|
+
const activeState = state;
|
|
569
|
+
clearTimeout(activeState.timeout);
|
|
570
|
+
activeState.timeout = setTimeout(() => {
|
|
571
|
+
this.delegationDebounceState.delete(key);
|
|
572
|
+
activeState.resolve();
|
|
573
|
+
}, DELEGATION_COMPLETION_DEBOUNCE_MS);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
span.addEvent("dispatch.delegation_debounce_scheduled", {
|
|
577
|
+
"debounce.ms": DELEGATION_COMPLETION_DEBOUNCE_MS,
|
|
578
|
+
"debounce.sequence": nextSequence,
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
await state.promise;
|
|
582
|
+
return nextSequence;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
private async dispatchToAgents(params: {
|
|
586
|
+
targetAgents: AgentInstance[];
|
|
587
|
+
event: NDKEvent;
|
|
588
|
+
conversationId: string;
|
|
589
|
+
projectCtx: ProjectContext;
|
|
590
|
+
agentExecutor: AgentExecutor;
|
|
591
|
+
parentSpan: ReturnType<typeof tracer.startSpan>;
|
|
592
|
+
}): Promise<void> {
|
|
593
|
+
const {
|
|
594
|
+
targetAgents,
|
|
595
|
+
event,
|
|
596
|
+
conversationId,
|
|
597
|
+
projectCtx,
|
|
598
|
+
agentExecutor,
|
|
599
|
+
parentSpan,
|
|
600
|
+
} = params;
|
|
601
|
+
const ralRegistry = RALRegistry.getInstance();
|
|
602
|
+
const dispatchContext = trace.setSpan(getSafeContext(), parentSpan);
|
|
603
|
+
|
|
604
|
+
// DIAGNOSTIC: Track concurrent execution metrics for bottleneck analysis
|
|
605
|
+
const dispatchStartTime = Date.now();
|
|
606
|
+
const currentActiveOps = llmOpsRegistry.getActiveOperationsCount();
|
|
607
|
+
parentSpan.addEvent("dispatch.concurrent_execution_starting", {
|
|
608
|
+
"concurrent.target_agents_count": targetAgents.length,
|
|
609
|
+
"concurrent.existing_active_ops": currentActiveOps,
|
|
610
|
+
"concurrent.total_after_dispatch": currentActiveOps + targetAgents.length,
|
|
611
|
+
"concurrent.dispatch_start_time": dispatchStartTime,
|
|
612
|
+
"concurrent.event_loop_lag_ms": this.measureEventLoopLag(),
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
const executionPromises = targetAgents.map(async (targetAgent) => {
|
|
616
|
+
const agentSpan = tracer.startSpan(
|
|
617
|
+
"tenex.dispatch.agent",
|
|
618
|
+
{
|
|
619
|
+
attributes: {
|
|
620
|
+
"agent.slug": targetAgent.slug,
|
|
621
|
+
"agent.pubkey": targetAgent.pubkey,
|
|
622
|
+
"conversation.id": shortenConversationId(conversationId),
|
|
623
|
+
},
|
|
624
|
+
},
|
|
625
|
+
dispatchContext
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
try {
|
|
629
|
+
// Check if this agent is in cooldown for this conversation
|
|
630
|
+
// Non-null assertion: conversationId and dTag are guaranteed to be defined from function params
|
|
631
|
+
const isInCooldown = await this.checkAndBlockIfCooldown(
|
|
632
|
+
projectCtx.project.dTag!,
|
|
633
|
+
conversationId!,
|
|
634
|
+
targetAgent.pubkey,
|
|
635
|
+
targetAgent.slug,
|
|
636
|
+
agentSpan,
|
|
637
|
+
"routing"
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
if (isInCooldown) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const activeRal = ralRegistry.getState(targetAgent.pubkey, conversationId);
|
|
645
|
+
|
|
646
|
+
agentSpan.setAttributes({
|
|
647
|
+
"ral.is_active": !!activeRal,
|
|
648
|
+
"ral.is_streaming": activeRal?.isStreaming ?? false,
|
|
649
|
+
"ral.number": activeRal?.ralNumber ?? 0,
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
const shouldSkipExecution = this.handleDeliveryInjection({
|
|
653
|
+
activeRal,
|
|
654
|
+
agent: targetAgent,
|
|
655
|
+
conversationId,
|
|
656
|
+
message: event.content,
|
|
657
|
+
senderPubkey: event.pubkey,
|
|
658
|
+
eventId: event.id,
|
|
659
|
+
agentSpan,
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
if (shouldSkipExecution) {
|
|
663
|
+
// Message was queued for an active streaming execution.
|
|
664
|
+
// Don't spawn a new execution - the active one will pick it up.
|
|
665
|
+
agentSpan.addEvent("dispatch.execution_skipped_injection_queued");
|
|
666
|
+
agentSpan.setStatus({ code: SpanStatusCode.OK });
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
let triggeringEventForContext = event;
|
|
671
|
+
const resumableRal = ralRegistry.findResumableRAL(targetAgent.pubkey, conversationId);
|
|
672
|
+
|
|
673
|
+
if (resumableRal?.originalTriggeringEventId) {
|
|
674
|
+
const originalEvent = ConversationStore.getCachedEvent(resumableRal.originalTriggeringEventId);
|
|
675
|
+
if (originalEvent) {
|
|
676
|
+
triggeringEventForContext = originalEvent;
|
|
677
|
+
getSafeActiveSpan()?.addEvent("reply.restored_original_trigger", {
|
|
678
|
+
"agent.slug": targetAgent.slug,
|
|
679
|
+
"original.event_id": resumableRal.originalTriggeringEventId,
|
|
680
|
+
"resumption.event_id": event.id || "",
|
|
681
|
+
});
|
|
682
|
+
agentSpan.addEvent("dispatch.restored_original_trigger", {
|
|
683
|
+
"original.event_id": resumableRal.originalTriggeringEventId,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const executionContext = await createExecutionContext({
|
|
689
|
+
agent: targetAgent,
|
|
690
|
+
conversationId,
|
|
691
|
+
projectBasePath: projectCtx.agentRegistry.getBasePath(),
|
|
692
|
+
triggeringEvent: triggeringEventForContext,
|
|
693
|
+
mcpManager: projectCtx.mcpManager,
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
// Execute within agentSpan context so agent.execute becomes a child span
|
|
697
|
+
await otelContext.with(trace.setSpan(otelContext.active(), agentSpan), async () => {
|
|
698
|
+
await agentExecutor.execute(executionContext);
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
agentSpan.setStatus({ code: SpanStatusCode.OK });
|
|
702
|
+
} catch (error) {
|
|
703
|
+
agentSpan.recordException(error as Error);
|
|
704
|
+
agentSpan.setStatus({
|
|
705
|
+
code: SpanStatusCode.ERROR,
|
|
706
|
+
message: (error as Error).message,
|
|
707
|
+
});
|
|
708
|
+
throw error;
|
|
709
|
+
} finally {
|
|
710
|
+
agentSpan.end();
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
await Promise.all(executionPromises);
|
|
715
|
+
|
|
716
|
+
// DIAGNOSTIC: Track concurrent execution completion metrics
|
|
717
|
+
const dispatchEndTime = Date.now();
|
|
718
|
+
const dispatchDuration = dispatchEndTime - dispatchStartTime;
|
|
719
|
+
const finalActiveOps = llmOpsRegistry.getActiveOperationsCount();
|
|
720
|
+
parentSpan.addEvent("dispatch.concurrent_execution_completed", {
|
|
721
|
+
"concurrent.dispatch_duration_ms": dispatchDuration,
|
|
722
|
+
"concurrent.agents_executed": targetAgents.length,
|
|
723
|
+
"concurrent.final_active_ops": finalActiveOps,
|
|
724
|
+
"concurrent.event_loop_lag_ms": this.measureEventLoopLag(),
|
|
725
|
+
"concurrent.avg_per_agent_ms": Math.round(dispatchDuration / targetAgents.length),
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Measure event loop lag to detect blocking operations.
|
|
731
|
+
* Returns the lag in milliseconds - high values indicate event loop blocking.
|
|
732
|
+
*/
|
|
733
|
+
private measureEventLoopLag(): number {
|
|
734
|
+
const start = process.hrtime.bigint();
|
|
735
|
+
// This is synchronous - it just measures how long since we scheduled vs now
|
|
736
|
+
// In real monitoring, you'd use setImmediate to measure actual lag
|
|
737
|
+
// For diagnostic purposes, this gives us a baseline timestamp
|
|
738
|
+
return Number((process.hrtime.bigint() - start) / 1000000n);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Check if an agent is in cooldown for a conversation and block routing if so.
|
|
743
|
+
* Returns true if the agent is in cooldown and routing should be blocked.
|
|
744
|
+
* Returns false if the agent is not in cooldown and routing should proceed.
|
|
745
|
+
*
|
|
746
|
+
* This helper consolidates the cooldown check logic used in multiple dispatch paths.
|
|
747
|
+
*/
|
|
748
|
+
private async checkAndBlockIfCooldown(
|
|
749
|
+
projectId: string,
|
|
750
|
+
conversationId: string,
|
|
751
|
+
agentPubkey: string,
|
|
752
|
+
agentSlug: string,
|
|
753
|
+
span: ReturnType<typeof tracer.startSpan>,
|
|
754
|
+
eventType: "delegation_completion" | "routing"
|
|
755
|
+
): Promise<boolean> {
|
|
756
|
+
const { CooldownRegistry } = await import("@/services/CooldownRegistry");
|
|
757
|
+
const cooldownRegistry = CooldownRegistry.getInstance();
|
|
758
|
+
|
|
759
|
+
if (cooldownRegistry.isInCooldown(projectId, conversationId, agentPubkey)) {
|
|
760
|
+
// Agent is in cooldown - skip routing
|
|
761
|
+
logger.info(`[dispatch] ${eventType === "delegation_completion" ? "Delegation completion routing" : "Routing"} blocked due to cooldown`, {
|
|
762
|
+
projectId: projectId.substring(0, 12),
|
|
763
|
+
conversationId: conversationId.substring(0, 12),
|
|
764
|
+
agentSlug,
|
|
765
|
+
agentPubkey: agentPubkey.substring(0, 12),
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
span.addEvent(`dispatch.${eventType}_blocked_cooldown`, {
|
|
769
|
+
"cooldown.project_id": projectId.substring(0, 12),
|
|
770
|
+
"cooldown.conversation_id": shortenConversationId(conversationId),
|
|
771
|
+
"cooldown.agent_pubkey": agentPubkey.substring(0, 12),
|
|
772
|
+
"cooldown.agent_slug": agentSlug,
|
|
773
|
+
});
|
|
774
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Handle injection of a message into an active RAL.
|
|
783
|
+
* Returns true if execution should be SKIPPED (message queued for active streaming execution).
|
|
784
|
+
* Returns false if a new execution should be spawned.
|
|
785
|
+
*/
|
|
786
|
+
private handleDeliveryInjection(params: {
|
|
787
|
+
activeRal: RALRegistryEntry | undefined;
|
|
788
|
+
agent: AgentInstance;
|
|
789
|
+
conversationId: string;
|
|
790
|
+
message: string;
|
|
791
|
+
senderPubkey: string;
|
|
792
|
+
eventId?: string;
|
|
793
|
+
agentSpan: ReturnType<typeof tracer.startSpan>;
|
|
794
|
+
}): boolean {
|
|
795
|
+
const {
|
|
796
|
+
activeRal,
|
|
797
|
+
agent,
|
|
798
|
+
conversationId,
|
|
799
|
+
message,
|
|
800
|
+
senderPubkey,
|
|
801
|
+
eventId,
|
|
802
|
+
agentSpan,
|
|
803
|
+
} = params;
|
|
804
|
+
|
|
805
|
+
if (!activeRal) {
|
|
806
|
+
return false; // No active RAL, spawn new execution
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const ralRegistry = RALRegistry.getInstance();
|
|
810
|
+
const messageLength = message.length;
|
|
811
|
+
|
|
812
|
+
ralRegistry.queueUserMessage(
|
|
813
|
+
agent.pubkey,
|
|
814
|
+
conversationId,
|
|
815
|
+
activeRal.ralNumber,
|
|
816
|
+
message,
|
|
817
|
+
{ senderPubkey, eventId }
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
if (activeRal.isStreaming) {
|
|
821
|
+
// Check if this is a Claude Code agent
|
|
822
|
+
const llmConfig = config.getLLMConfig(agent.llmConfig);
|
|
823
|
+
const isClaudeCodeProvider = llmConfig.provider === PROVIDER_IDS.CLAUDE_CODE;
|
|
824
|
+
|
|
825
|
+
if (isClaudeCodeProvider) {
|
|
826
|
+
// Try to use message injection if available
|
|
827
|
+
const injector = llmOpsRegistry.getMessageInjector(agent.pubkey, conversationId);
|
|
828
|
+
|
|
829
|
+
if (injector) {
|
|
830
|
+
// Message injector available - use it instead of abort-restart
|
|
831
|
+
injector.inject(message, (delivered) => {
|
|
832
|
+
if (delivered) {
|
|
833
|
+
// Clear queued injections since MessageInjector delivered the message directly.
|
|
834
|
+
// This prevents hasOutstandingWork() from incorrectly reporting pending work.
|
|
835
|
+
// See: naddr1qvzqqqr4gupzqkmm302xww6uyne99rnhl5kjj53wthjypm2qaem9uz9fdf3hzcf0qyghwumn8ghj7ar9dejhstnrdpshgtcq9p382emxd9uz6en0d3kx7am4wqkkjmn2v43hg6t0dckhzat9w4jj6cmvv4shy6twvullqw7x
|
|
836
|
+
ralRegistry.clearQueuedInjections(agent.pubkey, conversationId);
|
|
837
|
+
|
|
838
|
+
logger.info("[dispatch] Message injected via Claude Code MessageInjector", {
|
|
839
|
+
agent: agent.slug,
|
|
840
|
+
ralNumber: activeRal.ralNumber,
|
|
841
|
+
messageLength,
|
|
842
|
+
});
|
|
843
|
+
} else {
|
|
844
|
+
// Injection failed - the message is already queued in RALRegistry,
|
|
845
|
+
// so the next execution will pick it up
|
|
846
|
+
logger.warn("[dispatch] Message injection delivery failed (message already queued)", {
|
|
847
|
+
agent: agent.slug,
|
|
848
|
+
ralNumber: activeRal.ralNumber,
|
|
849
|
+
messageLength,
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
getSafeActiveSpan()?.addEvent("reply.message_injected_via_injector", {
|
|
855
|
+
"agent.slug": agent.slug,
|
|
856
|
+
"ral.number": activeRal.ralNumber,
|
|
857
|
+
"message.length": messageLength,
|
|
858
|
+
});
|
|
859
|
+
agentSpan.addEvent("dispatch.injection_via_message_injector", {
|
|
860
|
+
"message.length": messageLength,
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
// Skip spawning new execution - the active execution will pick up the injected message
|
|
864
|
+
return true;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// No injector available - fall back to abort-restart
|
|
868
|
+
const aborted = llmOpsRegistry.stopByAgentAndConversation(
|
|
869
|
+
agent.pubkey,
|
|
870
|
+
conversationId,
|
|
871
|
+
INJECTION_ABORT_REASON
|
|
872
|
+
);
|
|
873
|
+
|
|
874
|
+
if (aborted) {
|
|
875
|
+
getSafeActiveSpan()?.addEvent("reply.aborted_for_injection", {
|
|
876
|
+
"agent.slug": agent.slug,
|
|
877
|
+
"ral.number": activeRal.ralNumber,
|
|
878
|
+
"message.length": messageLength,
|
|
879
|
+
});
|
|
880
|
+
logger.info("[reply] Aborted Claude Code execution for injection (no injector)", {
|
|
881
|
+
agent: agent.slug,
|
|
882
|
+
ralNumber: activeRal.ralNumber,
|
|
883
|
+
injectionLength: messageLength,
|
|
884
|
+
});
|
|
885
|
+
agentSpan.addEvent("dispatch.injection_stream_abort_fallback", {
|
|
886
|
+
"message.length": messageLength,
|
|
887
|
+
});
|
|
888
|
+
} else {
|
|
889
|
+
getSafeActiveSpan()?.addEvent("reply.message_queued_during_streaming", {
|
|
890
|
+
"agent.slug": agent.slug,
|
|
891
|
+
"ral.number": activeRal.ralNumber,
|
|
892
|
+
"message.length": messageLength,
|
|
893
|
+
});
|
|
894
|
+
agentSpan.addEvent("dispatch.injection_stream_queue_only", {
|
|
895
|
+
"message.length": messageLength,
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
// Spawn new execution (aborted or will pick up on restart)
|
|
899
|
+
return false;
|
|
900
|
+
} else {
|
|
901
|
+
// Non-Claude-Code provider: just queue the message, don't abort.
|
|
902
|
+
// The active execution will pick it up on its next prepareStep.
|
|
903
|
+
// IMPORTANT: Skip spawning a new execution to avoid duplicate completions.
|
|
904
|
+
// See report: "injection-race-condition-hybrid-fix" for known limitations.
|
|
905
|
+
getSafeActiveSpan()?.addEvent("reply.message_injected_no_abort", {
|
|
906
|
+
"agent.slug": agent.slug,
|
|
907
|
+
"ral.number": activeRal.ralNumber,
|
|
908
|
+
"message.length": messageLength,
|
|
909
|
+
"provider": llmConfig.provider,
|
|
910
|
+
});
|
|
911
|
+
logger.info("[reply] Queued message for non-Claude-Code provider (no abort, skipping execution)", {
|
|
912
|
+
agent: agent.slug,
|
|
913
|
+
ralNumber: activeRal.ralNumber,
|
|
914
|
+
injectionLength: messageLength,
|
|
915
|
+
provider: llmConfig.provider,
|
|
916
|
+
});
|
|
917
|
+
agentSpan.addEvent("dispatch.injection_stream_no_abort_skip_execution", {
|
|
918
|
+
"message.length": messageLength,
|
|
919
|
+
"provider": llmConfig.provider,
|
|
920
|
+
});
|
|
921
|
+
// Non-Claude: skip execution, trust active execution to pick up injection
|
|
922
|
+
return true;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Not streaming (waiting for delegations) - need new execution to wake up
|
|
927
|
+
getSafeActiveSpan()?.addEvent("reply.message_queued_for_resumption", {
|
|
928
|
+
"agent.slug": agent.slug,
|
|
929
|
+
"ral.number": activeRal.ralNumber,
|
|
930
|
+
"message.length": messageLength,
|
|
931
|
+
});
|
|
932
|
+
agentSpan.addEvent("dispatch.injection_resumption", {
|
|
933
|
+
"message.length": messageLength,
|
|
934
|
+
});
|
|
935
|
+
return false; // Spawn new execution to wake up the waiting RAL
|
|
936
|
+
}
|
|
937
|
+
}
|