@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,561 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentExecutor - Orchestrates agent execution with LLM streaming
|
|
3
|
+
*
|
|
4
|
+
* This is the main entry point for executing agent tasks. It coordinates:
|
|
5
|
+
* - RAL (Request/Assignement/Loop) lifecycle management
|
|
6
|
+
* - Stream setup and execution via extracted modules
|
|
7
|
+
* - Post-completion supervision checks
|
|
8
|
+
* - Event publishing
|
|
9
|
+
*
|
|
10
|
+
* The heavy lifting is delegated to:
|
|
11
|
+
* - StreamSetup: Pre-stream configuration (tools, messages, injections)
|
|
12
|
+
* - StreamExecutionHandler: LLM streaming with event processing
|
|
13
|
+
* - PostCompletionChecker: Supervision heuristics
|
|
14
|
+
* - RALResolver: RAL lifecycle resolution
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { assertSupervisionHealth } from "@/agents/supervision";
|
|
18
|
+
import { checkPostCompletion } from "./PostCompletionChecker";
|
|
19
|
+
import { resolveRAL } from "./RALResolver";
|
|
20
|
+
import { ConversationStore } from "@/conversations/ConversationStore";
|
|
21
|
+
import { startExecutionTime, stopExecutionTime } from "@/conversations/executionTime";
|
|
22
|
+
import { formatAnyError } from "@/lib/error-formatter";
|
|
23
|
+
import { shortenConversationId } from "@/utils/conversation-id";
|
|
24
|
+
import { AgentPublisher } from "@/nostr/AgentPublisher";
|
|
25
|
+
import { INJECTION_ABORT_REASON } from "@/services/LLMOperationsRegistry";
|
|
26
|
+
import { getProjectContext } from "@/services/projects";
|
|
27
|
+
import { RALRegistry } from "@/services/ral";
|
|
28
|
+
import { getPubkeyService } from "@/services/PubkeyService";
|
|
29
|
+
import { getToolsObject } from "@/tools/registry";
|
|
30
|
+
import type { ToolRegistryContext } from "@/tools/types";
|
|
31
|
+
import { logger } from "@/utils/logger";
|
|
32
|
+
import { createEventContext } from "@/services/event-context";
|
|
33
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
34
|
+
import { SpanStatusCode, context as otelContext, trace } from "@opentelemetry/api";
|
|
35
|
+
import type { ModelMessage } from "ai";
|
|
36
|
+
import { ToolExecutionTracker } from "./ToolExecutionTracker";
|
|
37
|
+
import { setupStreamExecution } from "./StreamSetup";
|
|
38
|
+
import { StreamExecutionHandler } from "./StreamExecutionHandler";
|
|
39
|
+
import type {
|
|
40
|
+
ExecutionContext,
|
|
41
|
+
FullRuntimeContext,
|
|
42
|
+
LLMCompletionRequest,
|
|
43
|
+
StreamExecutionResult,
|
|
44
|
+
} from "./types";
|
|
45
|
+
|
|
46
|
+
const tracer = trace.getTracer("tenex.agent-executor");
|
|
47
|
+
|
|
48
|
+
export class AgentExecutor {
|
|
49
|
+
constructor() {
|
|
50
|
+
// Centralized supervision health check - ensures both total registry size AND
|
|
51
|
+
// post-completion heuristic count are validated (fail-closed semantics)
|
|
52
|
+
assertSupervisionHealth("AgentExecutor");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Warm user profile cache for injection sender pubkeys (best-effort, non-blocking).
|
|
57
|
+
*/
|
|
58
|
+
private warmSenderPubkeys(injections: Array<{ senderPubkey?: string }>): void {
|
|
59
|
+
const senderPubkeys = injections
|
|
60
|
+
.map((i) => i.senderPubkey)
|
|
61
|
+
.filter((pk): pk is string => !!pk);
|
|
62
|
+
|
|
63
|
+
if (senderPubkeys.length > 0) {
|
|
64
|
+
const pubkeyService = getPubkeyService();
|
|
65
|
+
void pubkeyService.warmUserProfiles(senderPubkeys).catch((error) => {
|
|
66
|
+
logger.debug("[AgentExecutor] Best-effort profile warming failed", {
|
|
67
|
+
error: error instanceof Error ? error.message : String(error),
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Prepare an LLM request without executing it.
|
|
75
|
+
* Creates stub context for tool schema extraction - runtime deps are never called.
|
|
76
|
+
*/
|
|
77
|
+
async prepareLLMRequest(
|
|
78
|
+
agent: { slug: string; tools?: string[] },
|
|
79
|
+
initialPrompt: string,
|
|
80
|
+
originalEvent: NDKEvent,
|
|
81
|
+
conversationHistory: ModelMessage[] = [],
|
|
82
|
+
projectPath?: string
|
|
83
|
+
): Promise<LLMCompletionRequest> {
|
|
84
|
+
const context: ToolRegistryContext = {
|
|
85
|
+
agent: agent as ToolRegistryContext["agent"],
|
|
86
|
+
triggeringEvent: originalEvent,
|
|
87
|
+
conversationId: originalEvent.id,
|
|
88
|
+
projectBasePath: projectPath || "",
|
|
89
|
+
workingDirectory: projectPath || "",
|
|
90
|
+
currentBranch: "main",
|
|
91
|
+
agentPublisher: {} as AgentPublisher,
|
|
92
|
+
ralNumber: 0,
|
|
93
|
+
conversationStore: {} as ConversationStore,
|
|
94
|
+
getConversation: () => ({} as ConversationStore),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const messages: ModelMessage[] = conversationHistory.length > 0
|
|
98
|
+
? [...conversationHistory]
|
|
99
|
+
: [{ role: "user", content: initialPrompt }];
|
|
100
|
+
|
|
101
|
+
const toolNames = agent.tools || [];
|
|
102
|
+
const tools = toolNames.length > 0 ? getToolsObject(toolNames, context) : {};
|
|
103
|
+
|
|
104
|
+
return { messages, tools };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Execute an agent's assignment for a conversation with streaming
|
|
109
|
+
*/
|
|
110
|
+
async execute(context: ExecutionContext): Promise<NDKEvent | undefined> {
|
|
111
|
+
const span = tracer.startSpan("tenex.agent.execute", {
|
|
112
|
+
attributes: {
|
|
113
|
+
"agent.slug": context.agent.slug,
|
|
114
|
+
"agent.pubkey": context.agent.pubkey,
|
|
115
|
+
"agent.role": context.agent.role || "worker",
|
|
116
|
+
"conversation.id": shortenConversationId(context.conversationId),
|
|
117
|
+
"triggering_event.id": context.triggeringEvent.id,
|
|
118
|
+
"triggering_event.kind": context.triggeringEvent.kind || 0,
|
|
119
|
+
},
|
|
120
|
+
}, otelContext.active());
|
|
121
|
+
|
|
122
|
+
return otelContext.with(trace.setSpan(otelContext.active(), span), async () => {
|
|
123
|
+
try {
|
|
124
|
+
// Get project ID for multi-project isolation in daemon mode
|
|
125
|
+
const projectCtx = getProjectContext();
|
|
126
|
+
const projectId = projectCtx.project.tagId();
|
|
127
|
+
|
|
128
|
+
const { ralNumber, isResumption, markersToPublish } = await resolveRAL({
|
|
129
|
+
agentPubkey: context.agent.pubkey,
|
|
130
|
+
conversationId: context.conversationId,
|
|
131
|
+
projectId,
|
|
132
|
+
triggeringEventId: context.triggeringEvent.id,
|
|
133
|
+
span,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// RACE CONDITION FIX: Early kill check
|
|
137
|
+
// If this conversation was killed before the agent started (or during RAL resolution),
|
|
138
|
+
// abort immediately without spending compute resources.
|
|
139
|
+
const ralRegistry = RALRegistry.getInstance();
|
|
140
|
+
if (ralRegistry.isAgentConversationKilled(context.agent.pubkey, context.conversationId)) {
|
|
141
|
+
span.addEvent("executor.aborted_early_kill", {
|
|
142
|
+
"ral.number": ralNumber,
|
|
143
|
+
"agent.pubkey": context.agent.pubkey.substring(0, 12),
|
|
144
|
+
"conversation.id": shortenConversationId(context.conversationId),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
logger.info("[AgentExecutor] Execution aborted - conversation was killed before agent started", {
|
|
148
|
+
agent: context.agent.slug,
|
|
149
|
+
conversationId: shortenConversationId(context.conversationId),
|
|
150
|
+
ralNumber,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Clean up the RAL we just created since we're not going to use it
|
|
154
|
+
ralRegistry.clear(context.agent.pubkey, context.conversationId);
|
|
155
|
+
|
|
156
|
+
span.setStatus({ code: SpanStatusCode.OK, message: "aborted_early_kill" });
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const contextWithRal = { ...context, ralNumber };
|
|
161
|
+
const { fullContext, toolTracker, agentPublisher, cleanup } =
|
|
162
|
+
this.prepareExecution(contextWithRal);
|
|
163
|
+
|
|
164
|
+
// Publish delegation marker updates to Nostr
|
|
165
|
+
// This happens after RAL resolution when delegations have completed
|
|
166
|
+
if (markersToPublish && markersToPublish.length > 0) {
|
|
167
|
+
span.addEvent("executor.publishing_delegation_markers", {
|
|
168
|
+
"marker.count": markersToPublish.length,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
for (const marker of markersToPublish) {
|
|
172
|
+
try {
|
|
173
|
+
await agentPublisher.delegationMarker(marker);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
logger.warn("Failed to publish delegation marker", {
|
|
176
|
+
delegationConversationId: marker.delegationConversationId.substring(0, 12),
|
|
177
|
+
status: marker.status,
|
|
178
|
+
error: formatAnyError(error),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const conversation = fullContext.getConversation();
|
|
185
|
+
if (conversation) {
|
|
186
|
+
span.setAttributes({
|
|
187
|
+
"conversation.message_count": conversation.getMessageCount(),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
span.addEvent("executor.started", {
|
|
192
|
+
ral_number: ralNumber,
|
|
193
|
+
is_resumption: isResumption,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const result = await this.executeOnce(
|
|
198
|
+
fullContext,
|
|
199
|
+
toolTracker,
|
|
200
|
+
agentPublisher,
|
|
201
|
+
ralNumber
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
205
|
+
return result;
|
|
206
|
+
} finally {
|
|
207
|
+
await cleanup();
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
span.recordException(error as Error);
|
|
211
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
212
|
+
|
|
213
|
+
const errorMessage = formatAnyError(error);
|
|
214
|
+
const isCreditsError =
|
|
215
|
+
errorMessage.includes("Insufficient credits") || errorMessage.includes("402");
|
|
216
|
+
|
|
217
|
+
const displayMessage = isCreditsError
|
|
218
|
+
? "Unable to process your request: Insufficient credits. Please add more credits at https://openrouter.ai/settings/credits to continue."
|
|
219
|
+
: `Unable to process your request due to an error: ${errorMessage}`;
|
|
220
|
+
|
|
221
|
+
const conversation = context.getConversation();
|
|
222
|
+
if (conversation) {
|
|
223
|
+
const agentPublisher = new AgentPublisher(context.agent);
|
|
224
|
+
try {
|
|
225
|
+
await agentPublisher.error(
|
|
226
|
+
{
|
|
227
|
+
message: displayMessage,
|
|
228
|
+
errorType: isCreditsError ? "insufficient_credits" : "execution_error",
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
triggeringEvent: context.triggeringEvent,
|
|
232
|
+
rootEvent: { id: conversation.getRootEventId() },
|
|
233
|
+
conversationId: conversation.id,
|
|
234
|
+
ralNumber: 0,
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
} catch (publishError) {
|
|
238
|
+
logger.error("Failed to publish execution error event", {
|
|
239
|
+
error: formatAnyError(publishError),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
logger.error(
|
|
245
|
+
isCreditsError
|
|
246
|
+
? "[AgentExecutor] Execution failed due to insufficient credits"
|
|
247
|
+
: "[AgentExecutor] Execution failed",
|
|
248
|
+
{
|
|
249
|
+
agent: context.agent.slug,
|
|
250
|
+
error: errorMessage,
|
|
251
|
+
conversationId: context.conversationId,
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
throw error;
|
|
256
|
+
} finally {
|
|
257
|
+
span.end();
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Prepare execution context with all necessary components.
|
|
264
|
+
*/
|
|
265
|
+
private prepareExecution(
|
|
266
|
+
context: ExecutionContext & { ralNumber: number }
|
|
267
|
+
): {
|
|
268
|
+
fullContext: FullRuntimeContext;
|
|
269
|
+
toolTracker: ToolExecutionTracker;
|
|
270
|
+
agentPublisher: AgentPublisher;
|
|
271
|
+
cleanup: () => Promise<void>;
|
|
272
|
+
} {
|
|
273
|
+
const toolTracker = new ToolExecutionTracker();
|
|
274
|
+
const agentPublisher = new AgentPublisher(context.agent);
|
|
275
|
+
const conversationStore = ConversationStore.getOrLoad(context.conversationId);
|
|
276
|
+
const projectContext = getProjectContext();
|
|
277
|
+
|
|
278
|
+
const fullContext: FullRuntimeContext = {
|
|
279
|
+
agent: context.agent,
|
|
280
|
+
conversationId: context.conversationId,
|
|
281
|
+
projectBasePath: context.projectBasePath,
|
|
282
|
+
workingDirectory: context.workingDirectory,
|
|
283
|
+
currentBranch: context.currentBranch,
|
|
284
|
+
triggeringEvent: context.triggeringEvent,
|
|
285
|
+
agentPublisher,
|
|
286
|
+
ralNumber: context.ralNumber,
|
|
287
|
+
conversationStore,
|
|
288
|
+
getConversation: () => conversationStore,
|
|
289
|
+
mcpManager: projectContext.mcpManager,
|
|
290
|
+
isDelegationCompletion: context.isDelegationCompletion,
|
|
291
|
+
hasPendingDelegations: context.hasPendingDelegations,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const conversation = fullContext.getConversation();
|
|
295
|
+
startExecutionTime(conversation);
|
|
296
|
+
|
|
297
|
+
const cleanup = async (): Promise<void> => {
|
|
298
|
+
stopExecutionTime(conversation);
|
|
299
|
+
toolTracker.clear();
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
return { fullContext, toolTracker, agentPublisher, cleanup };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Execute streaming and publish result
|
|
307
|
+
*/
|
|
308
|
+
private async executeOnce(
|
|
309
|
+
context: FullRuntimeContext,
|
|
310
|
+
toolTracker: ToolExecutionTracker,
|
|
311
|
+
agentPublisher: AgentPublisher,
|
|
312
|
+
ralNumber: number
|
|
313
|
+
): Promise<NDKEvent | undefined> {
|
|
314
|
+
let result: StreamExecutionResult;
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
result = await this.executeStreaming(context, toolTracker, ralNumber);
|
|
318
|
+
} catch (streamError) {
|
|
319
|
+
logger.error("[AgentExecutor] Streaming failed", {
|
|
320
|
+
agent: context.agent.slug,
|
|
321
|
+
error: formatAnyError(streamError),
|
|
322
|
+
});
|
|
323
|
+
throw streamError;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (result.kind === "error-handled") {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (result.aborted) {
|
|
331
|
+
if (result.abortReason === INJECTION_ABORT_REASON) {
|
|
332
|
+
trace.getActiveSpan()?.addEvent("executor.aborted_for_injection", {
|
|
333
|
+
"ral.number": ralNumber,
|
|
334
|
+
"agent.slug": context.agent.slug,
|
|
335
|
+
});
|
|
336
|
+
logger.info("[AgentExecutor] Execution aborted for injection - silent return", {
|
|
337
|
+
agent: context.agent.slug,
|
|
338
|
+
ralNumber,
|
|
339
|
+
});
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const eventContext = createEventContext(context);
|
|
344
|
+
const responseEvent = await agentPublisher.complete(
|
|
345
|
+
{ content: "Manually stopped by user" },
|
|
346
|
+
eventContext
|
|
347
|
+
);
|
|
348
|
+
// Handle case where completion was skipped (conversation was killed)
|
|
349
|
+
if (responseEvent) {
|
|
350
|
+
await ConversationStore.addEvent(context.conversationId, responseEvent);
|
|
351
|
+
}
|
|
352
|
+
return responseEvent;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const completionEvent = result.event;
|
|
356
|
+
const ralRegistry = RALRegistry.getInstance();
|
|
357
|
+
|
|
358
|
+
// =====================================================================
|
|
359
|
+
// RACE CONDITION FIX: Check for ANY outstanding work, not just pending delegations
|
|
360
|
+
// =====================================================================
|
|
361
|
+
// This is the key guard against the race condition where delegation results arrive
|
|
362
|
+
// (via debounce in AgentDispatchService) after the last prepareStep but before
|
|
363
|
+
// the executor finalizes. The debounce state queues injections that would be
|
|
364
|
+
// invisible if we only checked pendingDelegations.
|
|
365
|
+
//
|
|
366
|
+
// hasOutstandingWork() consolidates checking for:
|
|
367
|
+
// 1. Queued injections (messages/delegation results waiting for next LLM step)
|
|
368
|
+
// 2. Pending delegations (delegations that haven't completed yet)
|
|
369
|
+
// =====================================================================
|
|
370
|
+
const outstandingWork = ralRegistry.hasOutstandingWork(
|
|
371
|
+
context.agent.pubkey,
|
|
372
|
+
context.conversationId,
|
|
373
|
+
ralNumber
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
// NOTE: startedWithPendingDelegations is a snapshot from dispatch time, used ONLY for
|
|
377
|
+
// conservative RAL lifetime management (line ~395). It should NOT be used for the publish
|
|
378
|
+
// mode decision because delegations may have completed during execution.
|
|
379
|
+
const startedWithPendingDelegations = Boolean(
|
|
380
|
+
context.isDelegationCompletion && context.hasPendingDelegations
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// DIAGNOSTIC: Trace the exact values used in outstanding work decision
|
|
384
|
+
trace.getActiveSpan()?.addEvent("executor.outstanding_work_decision", {
|
|
385
|
+
"context.isDelegationCompletion": context.isDelegationCompletion ?? false,
|
|
386
|
+
"context.hasPendingDelegations_snapshot": context.hasPendingDelegations ?? false,
|
|
387
|
+
"startedWithPendingDelegations_for_ral_cleanup": startedWithPendingDelegations,
|
|
388
|
+
"outstanding.has_work": outstandingWork.hasWork,
|
|
389
|
+
"outstanding.queued_injections": outstandingWork.details.queuedInjections,
|
|
390
|
+
"outstanding.pending_delegations": outstandingWork.details.pendingDelegations,
|
|
391
|
+
"fix_applied": "uses hasOutstandingWork() to check both injections and delegations",
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
// INVARIANT GUARD: If there's outstanding work (queued injections OR pending delegations),
|
|
395
|
+
// we should NOT finalize. The executor should continue to allow the work to be processed.
|
|
396
|
+
const hasMessageContent = completionEvent?.message && completionEvent.message.length > 0;
|
|
397
|
+
if (!hasMessageContent && outstandingWork.hasWork) {
|
|
398
|
+
// This is the expected path when delegation results arrive via debounce.
|
|
399
|
+
// The executor returns undefined to allow the dispatch loop to continue
|
|
400
|
+
// and process the queued injections in the next iteration.
|
|
401
|
+
trace.getActiveSpan()?.addEvent("executor.awaiting_outstanding_work", {
|
|
402
|
+
"ral.number": ralNumber,
|
|
403
|
+
"outstanding.queued_injections": outstandingWork.details.queuedInjections,
|
|
404
|
+
"outstanding.pending_delegations": outstandingWork.details.pendingDelegations,
|
|
405
|
+
"completion_event_exists": Boolean(completionEvent),
|
|
406
|
+
"scenario": "injection_debounce_await",
|
|
407
|
+
});
|
|
408
|
+
logger.debug("[AgentExecutor] Deferring finalization due to outstanding work", {
|
|
409
|
+
agent: context.agent.slug,
|
|
410
|
+
ralNumber,
|
|
411
|
+
queuedInjections: outstandingWork.details.queuedInjections,
|
|
412
|
+
pendingDelegations: outstandingWork.details.pendingDelegations,
|
|
413
|
+
});
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (!completionEvent) {
|
|
418
|
+
// This is an unexpected state: no completion event AND no outstanding work.
|
|
419
|
+
// The LLM stream should always produce a completion event if it completes normally.
|
|
420
|
+
// Log extensively before throwing to aid debugging.
|
|
421
|
+
logger.error("[AgentExecutor] Missing completion event with no outstanding work", {
|
|
422
|
+
agent: context.agent.slug,
|
|
423
|
+
ralNumber,
|
|
424
|
+
conversationId: context.conversationId.substring(0, 12),
|
|
425
|
+
hasOutstandingWork: outstandingWork.hasWork,
|
|
426
|
+
});
|
|
427
|
+
trace.getActiveSpan()?.addEvent("executor.missing_completion_event_error", {
|
|
428
|
+
"ral.number": ralNumber,
|
|
429
|
+
"outstanding.has_work": outstandingWork.hasWork,
|
|
430
|
+
"scenario": "unexpected_missing_completion",
|
|
431
|
+
});
|
|
432
|
+
throw new Error("LLM execution completed without producing a completion event");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Post-completion supervision check
|
|
436
|
+
const supervisionCheckResult = await checkPostCompletion({
|
|
437
|
+
agent: context.agent,
|
|
438
|
+
context,
|
|
439
|
+
conversationStore: context.conversationStore,
|
|
440
|
+
ralNumber,
|
|
441
|
+
completionEvent,
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
if (supervisionCheckResult.shouldReEngage) {
|
|
445
|
+
return this.executeOnce(context, toolTracker, agentPublisher, ralNumber);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// RAL cleanup - use hasOutstandingWork for comprehensive check
|
|
449
|
+
const conversationStore = context.conversationStore;
|
|
450
|
+
const cleanupOutstandingWork = ralRegistry.hasOutstandingWork(
|
|
451
|
+
context.agent.pubkey,
|
|
452
|
+
context.conversationId,
|
|
453
|
+
ralNumber
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
if (!cleanupOutstandingWork.hasWork && !startedWithPendingDelegations) {
|
|
457
|
+
ralRegistry.clearRAL(context.agent.pubkey, context.conversationId, ralNumber);
|
|
458
|
+
conversationStore.completeRal(context.agent.pubkey, ralNumber);
|
|
459
|
+
await conversationStore.save();
|
|
460
|
+
|
|
461
|
+
trace.getActiveSpan()?.addEvent("executor.ral_cleared_post_supervision_check", {
|
|
462
|
+
"ral.number": ralNumber,
|
|
463
|
+
"supervision.executed": true,
|
|
464
|
+
"supervision.had_violation": supervisionCheckResult.shouldReEngage,
|
|
465
|
+
});
|
|
466
|
+
} else if (!cleanupOutstandingWork.hasWork && startedWithPendingDelegations) {
|
|
467
|
+
trace.getActiveSpan()?.addEvent("executor.ral_clear_skipped_pending_at_start", {
|
|
468
|
+
"ral.number": ralNumber,
|
|
469
|
+
"supervision.executed": true,
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const eventContext = createEventContext(context, {
|
|
474
|
+
model: completionEvent?.usage?.model,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// Re-check outstanding work for final publish decision (state may have changed after supervision)
|
|
478
|
+
const finalOutstandingWork = ralRegistry.hasOutstandingWork(
|
|
479
|
+
context.agent.pubkey,
|
|
480
|
+
context.conversationId,
|
|
481
|
+
ralNumber
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
trace.getActiveSpan()?.addEvent("executor.publish", {
|
|
485
|
+
"message.length": completionEvent?.message?.length || 0,
|
|
486
|
+
"outstanding.has_work": finalOutstandingWork.hasWork,
|
|
487
|
+
"outstanding.queued_injections": finalOutstandingWork.details.queuedInjections,
|
|
488
|
+
"outstanding.pending_delegations": finalOutstandingWork.details.pendingDelegations,
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
let responseEvent: NDKEvent | undefined;
|
|
492
|
+
|
|
493
|
+
if (finalOutstandingWork.hasWork) {
|
|
494
|
+
// If there's outstanding work, publish as conversation (not completion)
|
|
495
|
+
if (completionEvent.message.trim().length > 0) {
|
|
496
|
+
responseEvent = await agentPublisher.conversation(
|
|
497
|
+
{ content: completionEvent.message, usage: completionEvent.usage },
|
|
498
|
+
eventContext
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
// No outstanding work - safe to publish as complete
|
|
503
|
+
responseEvent = await agentPublisher.complete(
|
|
504
|
+
{ content: completionEvent.message, usage: completionEvent.usage },
|
|
505
|
+
eventContext
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (responseEvent) {
|
|
510
|
+
await ConversationStore.addEvent(context.conversationId, responseEvent);
|
|
511
|
+
|
|
512
|
+
trace.getActiveSpan()?.addEvent("executor.published", {
|
|
513
|
+
"event.id": responseEvent.id || "",
|
|
514
|
+
is_completion: !finalOutstandingWork.hasWork,
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
result.messageCompiler.advanceCursor();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return responseEvent;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Execute streaming and return the result.
|
|
525
|
+
* Delegates to StreamSetup for configuration and StreamExecutionHandler for execution.
|
|
526
|
+
*/
|
|
527
|
+
private async executeStreaming(
|
|
528
|
+
context: FullRuntimeContext,
|
|
529
|
+
toolTracker: ToolExecutionTracker,
|
|
530
|
+
ralNumber: number
|
|
531
|
+
): Promise<StreamExecutionResult> {
|
|
532
|
+
// Setup stream execution (tools, messages, injections, meta model)
|
|
533
|
+
const setup = await setupStreamExecution(
|
|
534
|
+
context,
|
|
535
|
+
toolTracker,
|
|
536
|
+
ralNumber,
|
|
537
|
+
{ warmSenderPubkeys: this.warmSenderPubkeys.bind(this) }
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
// Create and execute stream handler
|
|
541
|
+
const handler = new StreamExecutionHandler({
|
|
542
|
+
context,
|
|
543
|
+
toolTracker,
|
|
544
|
+
ralNumber,
|
|
545
|
+
toolsObject: setup.toolsObject,
|
|
546
|
+
sessionManager: setup.sessionManager,
|
|
547
|
+
llmService: setup.llmService,
|
|
548
|
+
messageCompiler: setup.messageCompiler,
|
|
549
|
+
nudgeContent: setup.nudgeContent,
|
|
550
|
+
skillContent: setup.skillContent,
|
|
551
|
+
skills: setup.skills,
|
|
552
|
+
ephemeralMessages: setup.ephemeralMessages,
|
|
553
|
+
abortSignal: setup.abortSignal,
|
|
554
|
+
metaModelSystemPrompt: setup.metaModelSystemPrompt,
|
|
555
|
+
variantSystemPrompt: setup.variantSystemPrompt,
|
|
556
|
+
compressionLlmService: setup.compressionLlmService,
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
return handler.execute(setup.messages);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { AgentInstance } from "@/agents/types";
|
|
2
|
+
import { ConversationStore } from "@/conversations/ConversationStore";
|
|
3
|
+
import type { AgentPublisher } from "@/nostr/AgentPublisher";
|
|
4
|
+
import type { MCPManager } from "@/services/mcp/MCPManager";
|
|
5
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
6
|
+
import { listWorktrees, createWorktree } from "@/utils/git/worktree";
|
|
7
|
+
import { getCurrentBranchWithFallback } from "@/utils/git/initializeGitRepo";
|
|
8
|
+
import { logger } from "@/utils/logger";
|
|
9
|
+
import type { ExecutionContext } from "./types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create an ExecutionContext with environment resolution from event
|
|
13
|
+
*
|
|
14
|
+
* This factory resolves the working directory based on the triggering event's branch tag:
|
|
15
|
+
* - No branch tag: Uses projectBasePath directly (main repo with default branch)
|
|
16
|
+
* - With branch tag: Uses .worktrees/{sanitized_branch}/ directory
|
|
17
|
+
*
|
|
18
|
+
* @param params Context creation parameters
|
|
19
|
+
* @returns Complete ExecutionContext with resolved environment
|
|
20
|
+
*/
|
|
21
|
+
export async function createExecutionContext(params: {
|
|
22
|
+
agent: AgentInstance;
|
|
23
|
+
conversationId: string;
|
|
24
|
+
/**
|
|
25
|
+
* Project directory (normal git repository root).
|
|
26
|
+
* Example: ~/tenex/{dTag}
|
|
27
|
+
*/
|
|
28
|
+
projectBasePath: string;
|
|
29
|
+
triggeringEvent: NDKEvent;
|
|
30
|
+
agentPublisher?: AgentPublisher;
|
|
31
|
+
isDelegationCompletion?: boolean;
|
|
32
|
+
hasPendingDelegations?: boolean;
|
|
33
|
+
debug?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* MCP manager for this project's MCP tool access.
|
|
36
|
+
* Required for agents to use MCP tools at execution time.
|
|
37
|
+
*/
|
|
38
|
+
mcpManager?: MCPManager;
|
|
39
|
+
}): Promise<ExecutionContext> {
|
|
40
|
+
// Extract branch tag from event
|
|
41
|
+
const branchTag = params.triggeringEvent.tags.find(t => t[0] === "branch")?.[1];
|
|
42
|
+
|
|
43
|
+
// Resolve execution environment
|
|
44
|
+
let workingDirectory: string;
|
|
45
|
+
let currentBranch: string;
|
|
46
|
+
|
|
47
|
+
if (branchTag) {
|
|
48
|
+
// Branch specified in event - check if it's in a worktree
|
|
49
|
+
const worktrees = await listWorktrees(params.projectBasePath);
|
|
50
|
+
const matchingWorktree = worktrees.find(wt => wt.branch === branchTag);
|
|
51
|
+
|
|
52
|
+
if (matchingWorktree) {
|
|
53
|
+
// Found the worktree
|
|
54
|
+
workingDirectory = matchingWorktree.path;
|
|
55
|
+
currentBranch = branchTag;
|
|
56
|
+
logger.info("Using worktree from branch tag", {
|
|
57
|
+
branch: branchTag,
|
|
58
|
+
path: matchingWorktree.path
|
|
59
|
+
});
|
|
60
|
+
} else {
|
|
61
|
+
// Worktree not found - create it now
|
|
62
|
+
const baseBranch = await getCurrentBranchWithFallback(params.projectBasePath);
|
|
63
|
+
|
|
64
|
+
logger.info("Branch tag specified but worktree not found, creating it", {
|
|
65
|
+
branch: branchTag,
|
|
66
|
+
baseBranch,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
workingDirectory = await createWorktree(params.projectBasePath, branchTag, baseBranch);
|
|
71
|
+
currentBranch = branchTag;
|
|
72
|
+
|
|
73
|
+
logger.info("Created worktree for delegation", {
|
|
74
|
+
branch: branchTag,
|
|
75
|
+
path: workingDirectory,
|
|
76
|
+
baseBranch,
|
|
77
|
+
});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
// If worktree creation fails, fall back to project root with a warning
|
|
80
|
+
logger.error("Failed to create worktree, falling back to project root", {
|
|
81
|
+
branch: branchTag,
|
|
82
|
+
error: error instanceof Error ? error.message : String(error),
|
|
83
|
+
});
|
|
84
|
+
workingDirectory = params.projectBasePath;
|
|
85
|
+
currentBranch = baseBranch;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// No branch tag - use project root (default branch)
|
|
90
|
+
workingDirectory = params.projectBasePath;
|
|
91
|
+
currentBranch = await getCurrentBranchWithFallback(params.projectBasePath);
|
|
92
|
+
logger.info("Using project root as working directory", {
|
|
93
|
+
path: workingDirectory,
|
|
94
|
+
branch: currentBranch
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
agent: params.agent,
|
|
100
|
+
conversationId: params.conversationId,
|
|
101
|
+
projectBasePath: params.projectBasePath,
|
|
102
|
+
workingDirectory,
|
|
103
|
+
currentBranch,
|
|
104
|
+
triggeringEvent: params.triggeringEvent,
|
|
105
|
+
agentPublisher: params.agentPublisher,
|
|
106
|
+
isDelegationCompletion: params.isDelegationCompletion,
|
|
107
|
+
hasPendingDelegations: params.hasPendingDelegations,
|
|
108
|
+
debug: params.debug,
|
|
109
|
+
mcpManager: params.mcpManager,
|
|
110
|
+
getConversation: () => ConversationStore.get(params.conversationId),
|
|
111
|
+
};
|
|
112
|
+
}
|