@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,181 @@
|
|
|
1
|
+
import type { AgentInstance } from "@/agents/types";
|
|
2
|
+
import type { ConversationStore } from "@/conversations/ConversationStore";
|
|
3
|
+
import { AgentEventDecoder } from "@/nostr/AgentEventDecoder";
|
|
4
|
+
import type { ProjectContext } from "@/services/projects";
|
|
5
|
+
import { logger } from "@/utils/logger";
|
|
6
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import type { DelegationCompletionResult } from "./DelegationCompletionHandler";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* AgentRouter is a static utility class that determines which agent
|
|
12
|
+
* should handle an incoming event. This centralizes the routing logic
|
|
13
|
+
* that was previously embedded in reply.ts.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
|
|
17
|
+
export class AgentRouter {
|
|
18
|
+
/**
|
|
19
|
+
* Process a stop signal (kind 24134) to block an agent in a conversation.
|
|
20
|
+
* Returns { blocked: true } if the agent was blocked.
|
|
21
|
+
*/
|
|
22
|
+
static processStopSignal(
|
|
23
|
+
event: NDKEvent,
|
|
24
|
+
conversation: ConversationStore,
|
|
25
|
+
projectContext: ProjectContext
|
|
26
|
+
): { blocked: boolean } {
|
|
27
|
+
const pTags = event.getMatchingTags("p");
|
|
28
|
+
|
|
29
|
+
for (const [, agentPubkey] of pTags) {
|
|
30
|
+
const agent = projectContext.getAgentByPubkey(agentPubkey);
|
|
31
|
+
if (agent) {
|
|
32
|
+
conversation.blockAgent(agentPubkey);
|
|
33
|
+
logger.info(
|
|
34
|
+
chalk.yellow(
|
|
35
|
+
`Blocked agent ${agent.slug} in conversation ${conversation.id.substring(0, 8)}`
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { blocked: pTags.length > 0 };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Determine which agents should handle the event based on p-tags,
|
|
46
|
+
* event author, and other context.
|
|
47
|
+
*
|
|
48
|
+
* @param event - The incoming event
|
|
49
|
+
* @param projectContext - Project context with agent information
|
|
50
|
+
* @param conversation - Optional conversation to check for blocked agents
|
|
51
|
+
* @returns Array of target agents that should process this event
|
|
52
|
+
*/
|
|
53
|
+
static resolveTargetAgents(
|
|
54
|
+
event: NDKEvent,
|
|
55
|
+
projectContext: ProjectContext,
|
|
56
|
+
conversation?: ConversationStore
|
|
57
|
+
): AgentInstance[] {
|
|
58
|
+
const mentionedPubkeys = AgentEventDecoder.getMentionedPubkeys(event);
|
|
59
|
+
|
|
60
|
+
// Check if the event author is an agent in the system
|
|
61
|
+
const isAuthorAnAgent = AgentEventDecoder.isEventFromAgent(event, projectContext.agents);
|
|
62
|
+
|
|
63
|
+
// Check for p-tagged agents regardless of sender
|
|
64
|
+
if (mentionedPubkeys.length > 0) {
|
|
65
|
+
// Find ALL p-tagged system agents
|
|
66
|
+
const targetAgents: AgentInstance[] = [];
|
|
67
|
+
for (const pubkey of mentionedPubkeys) {
|
|
68
|
+
// Skip blocked agents
|
|
69
|
+
if (conversation?.isAgentBlocked(pubkey)) {
|
|
70
|
+
const agent = projectContext.getAgentByPubkey(pubkey);
|
|
71
|
+
logger.info(
|
|
72
|
+
chalk.yellow(
|
|
73
|
+
`Skipping blocked agent ${agent?.slug ?? pubkey.substring(0, 8)} in conversation ${conversation.id.substring(0, 8)}`
|
|
74
|
+
)
|
|
75
|
+
);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const agent = projectContext.getAgentByPubkey(pubkey);
|
|
80
|
+
if (agent) {
|
|
81
|
+
targetAgents.push(agent);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (targetAgents.length > 0) {
|
|
86
|
+
const agentNames = targetAgents.map((a) => a.name).join(", ");
|
|
87
|
+
logger.info(
|
|
88
|
+
chalk.gray(`Routing to ${targetAgents.length} p-tagged agent(s): ${agentNames}`)
|
|
89
|
+
);
|
|
90
|
+
return targetAgents;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// If no p-tags, don't route to anyone - just log it
|
|
95
|
+
if (mentionedPubkeys.length === 0) {
|
|
96
|
+
const senderType = isAuthorAnAgent ? "agent" : "user";
|
|
97
|
+
logger.info(
|
|
98
|
+
chalk.gray(
|
|
99
|
+
`Event from ${senderType} ${event.pubkey.substring(0, 8)} without p-tags - not routing to any agent`
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Unblock an agent in a conversation if the sender is whitelisted.
|
|
110
|
+
* Returns { unblocked: true } if successful.
|
|
111
|
+
*/
|
|
112
|
+
static unblockAgent(
|
|
113
|
+
event: NDKEvent,
|
|
114
|
+
conversation: ConversationStore,
|
|
115
|
+
projectContext: ProjectContext,
|
|
116
|
+
whitelist: Set<string>
|
|
117
|
+
): { unblocked: boolean } {
|
|
118
|
+
// Only whitelisted pubkeys can unblock
|
|
119
|
+
if (!whitelist.has(event.pubkey)) {
|
|
120
|
+
return { unblocked: false };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const pTags = event.getMatchingTags("p");
|
|
124
|
+
let unblocked = false;
|
|
125
|
+
|
|
126
|
+
for (const [, agentPubkey] of pTags) {
|
|
127
|
+
if (conversation.isAgentBlocked(agentPubkey)) {
|
|
128
|
+
conversation.unblockAgent(agentPubkey);
|
|
129
|
+
const agent = projectContext.getAgentByPubkey(agentPubkey);
|
|
130
|
+
logger.info(
|
|
131
|
+
chalk.green(
|
|
132
|
+
`Unblocked agent ${agent?.slug ?? agentPubkey.substring(0, 8)} in conversation ${conversation.id.substring(0, 8)} by ${event.pubkey.substring(0, 8)}`
|
|
133
|
+
)
|
|
134
|
+
);
|
|
135
|
+
unblocked = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { unblocked };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Resolve routing target for a delegation completion.
|
|
144
|
+
* Returns the agent and conversation ID where the waiting RAL lives,
|
|
145
|
+
* or null if the delegation wasn't recorded or agent not found.
|
|
146
|
+
*/
|
|
147
|
+
static resolveDelegationTarget(
|
|
148
|
+
delegationResult: DelegationCompletionResult,
|
|
149
|
+
projectContext: ProjectContext
|
|
150
|
+
): { agent: AgentInstance; conversationId: string } | null {
|
|
151
|
+
if (!delegationResult.recorded) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const { agentSlug, conversationId } = delegationResult;
|
|
156
|
+
if (!agentSlug || !conversationId) {
|
|
157
|
+
logger.warn(
|
|
158
|
+
chalk.yellow(
|
|
159
|
+
"[AgentRouter] Delegation recorded but missing agentSlug or conversationId"
|
|
160
|
+
)
|
|
161
|
+
);
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const waitingAgent = projectContext.getAgent(agentSlug);
|
|
166
|
+
if (!waitingAgent) {
|
|
167
|
+
logger.warn(
|
|
168
|
+
chalk.yellow(`[AgentRouter] Waiting agent not found: ${agentSlug}`)
|
|
169
|
+
);
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
logger.info(
|
|
174
|
+
chalk.gray(
|
|
175
|
+
`Routing delegation completion to ${agentSlug} in conversation ${conversationId.substring(0, 8)}`
|
|
176
|
+
)
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
return { agent: waitingAgent, conversationId };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { TagExtractor } from "@/nostr/TagExtractor";
|
|
2
|
+
import { getProjectContext } from "@/services/projects";
|
|
3
|
+
import { logger } from "@/utils/logger";
|
|
4
|
+
import { shortenConversationId } from "@/utils/conversation-id";
|
|
5
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
6
|
+
import { RALRegistry } from "@/services/ral";
|
|
7
|
+
import { ConversationStore } from "@/conversations/ConversationStore";
|
|
8
|
+
import { trace, SpanStatusCode, context as otelContext } from "@opentelemetry/api";
|
|
9
|
+
|
|
10
|
+
const tracer = trace.getTracer("tenex.delegation");
|
|
11
|
+
|
|
12
|
+
export interface DelegationCompletionResult {
|
|
13
|
+
/** Whether a completion was recorded */
|
|
14
|
+
recorded: boolean;
|
|
15
|
+
/** The agent slug that's waiting for this delegation (if any) */
|
|
16
|
+
agentSlug?: string;
|
|
17
|
+
/** The conversation ID where the delegation was made */
|
|
18
|
+
conversationId?: string;
|
|
19
|
+
/** Number of pending delegations remaining */
|
|
20
|
+
pendingCount?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Record a delegation completion event in RALRegistry.
|
|
25
|
+
* Does NOT handle routing or resumption - that's handled by normal routing + AgentExecutor.
|
|
26
|
+
*
|
|
27
|
+
* Flow:
|
|
28
|
+
* 1. Find which delegation this responds to (via e-tag)
|
|
29
|
+
* 2. Look up which RAL is waiting for this delegation
|
|
30
|
+
* 3. Record the completion
|
|
31
|
+
*/
|
|
32
|
+
export async function handleDelegationCompletion(
|
|
33
|
+
event: NDKEvent
|
|
34
|
+
): Promise<DelegationCompletionResult> {
|
|
35
|
+
// Early exit: check for e-tags BEFORE creating a span to avoid trace noise
|
|
36
|
+
// Events without e-tags cannot be delegation completions
|
|
37
|
+
const eTags = TagExtractor.getETags(event);
|
|
38
|
+
if (eTags.length === 0) {
|
|
39
|
+
return { recorded: false };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const span = tracer.startSpan("tenex.delegation.completion_check", {
|
|
43
|
+
attributes: {
|
|
44
|
+
"event.id": event.id || "",
|
|
45
|
+
"event.pubkey": event.pubkey,
|
|
46
|
+
"event.kind": event.kind || 0,
|
|
47
|
+
"delegation.etag_count": eTags.length,
|
|
48
|
+
},
|
|
49
|
+
}, otelContext.active());
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const ralRegistry = RALRegistry.getInstance();
|
|
53
|
+
|
|
54
|
+
// Try e-tags in reverse order (last to first) as per NIP-10 convention
|
|
55
|
+
// The last e-tag is typically the direct reply target in threaded conversations
|
|
56
|
+
let location = null;
|
|
57
|
+
let delegationEventId = null;
|
|
58
|
+
|
|
59
|
+
for (let i = eTags.length - 1; i >= 0; i--) {
|
|
60
|
+
const eTag = eTags[i];
|
|
61
|
+
span.addEvent("trying_delegation_etag", {
|
|
62
|
+
"delegation.event_id": eTag,
|
|
63
|
+
"etag.index": i,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Look up the pending delegation to get context for validation
|
|
67
|
+
const pendingInfo = ralRegistry.findDelegation(eTag);
|
|
68
|
+
|
|
69
|
+
if (!pendingInfo?.pending) {
|
|
70
|
+
// No pending delegation found for this e-tag, try next one
|
|
71
|
+
span.addEvent("no_pending_delegation", {
|
|
72
|
+
"delegation.event_id": eTag,
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Validate that the event author is the delegated agent
|
|
78
|
+
// This prevents OTHER agents from falsely completing delegations
|
|
79
|
+
if (event.pubkey !== pendingInfo.pending.recipientPubkey) {
|
|
80
|
+
span.addEvent("completion_sender_mismatch", {
|
|
81
|
+
"delegation.event_id": eTag,
|
|
82
|
+
"expected.recipient_pubkey": pendingInfo.pending.recipientPubkey.substring(0, 12),
|
|
83
|
+
"actual.sender_pubkey": event.pubkey.substring(0, 12),
|
|
84
|
+
"validation.matched": false,
|
|
85
|
+
});
|
|
86
|
+
logger.debug("[handleDelegationCompletion] Ignoring event - sender is not the delegated agent", {
|
|
87
|
+
delegationEventId: eTag.substring(0, 8),
|
|
88
|
+
expectedRecipient: pendingInfo.pending.recipientPubkey.substring(0, 12),
|
|
89
|
+
actualSender: event.pubkey.substring(0, 12),
|
|
90
|
+
});
|
|
91
|
+
continue; // Skip to next e-tag
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
span.addEvent("completion_sender_validated", {
|
|
95
|
+
"delegation.event_id": eTag,
|
|
96
|
+
"expected.recipient_pubkey": pendingInfo.pending.recipientPubkey.substring(0, 12),
|
|
97
|
+
"actual.sender_pubkey": event.pubkey.substring(0, 12),
|
|
98
|
+
"validation.matched": true,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// DEFENSE-IN-DEPTH: Early exit for killed delegations.
|
|
102
|
+
// The authoritative check is in RALRegistry.recordCompletion(), but we check
|
|
103
|
+
// here too using the isDelegationKilled() helper to avoid unnecessary work.
|
|
104
|
+
if (ralRegistry.isDelegationKilled(eTag)) {
|
|
105
|
+
span.addEvent("completion_skipped_delegation_killed", {
|
|
106
|
+
"delegation.event_id": eTag,
|
|
107
|
+
"delegation.killed_at": pendingInfo.pending.killedAt,
|
|
108
|
+
});
|
|
109
|
+
logger.info("[handleDelegationCompletion] Ignoring completion - delegation was killed", {
|
|
110
|
+
delegationEventId: eTag.substring(0, 8),
|
|
111
|
+
killedAt: pendingInfo.pending.killedAt,
|
|
112
|
+
completionEventId: event.id?.substring(0, 8),
|
|
113
|
+
});
|
|
114
|
+
continue; // Skip to next e-tag - this delegation was killed
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Record the completion (looks up RAL internally via delegation conversation ID)
|
|
118
|
+
// NOTE: We no longer build/pass fullTranscript here. The marker-based system
|
|
119
|
+
// (RALResolver + MessageBuilder) reads the conversation transcript directly from
|
|
120
|
+
// ConversationStore when expanding delegation markers. Storing redundant transcripts
|
|
121
|
+
// in CompletedDelegation was causing unnecessary memory/disk bloat.
|
|
122
|
+
const result = ralRegistry.recordCompletion({
|
|
123
|
+
delegationConversationId: eTag,
|
|
124
|
+
recipientPubkey: event.pubkey,
|
|
125
|
+
response: event.content,
|
|
126
|
+
completedAt: Math.floor(Date.now() / 1000), // Use seconds to match pending markers
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (result) {
|
|
130
|
+
location = result;
|
|
131
|
+
delegationEventId = eTag;
|
|
132
|
+
|
|
133
|
+
// Add the completion event to the delegation conversation store.
|
|
134
|
+
// This ensures getDelegationMessages() can return the user's response
|
|
135
|
+
// when building the delegation marker transcript. Without this, ask
|
|
136
|
+
// conversations would show "transcript unavailable" instead of the
|
|
137
|
+
// actual user response.
|
|
138
|
+
// See: naddr1qvzqqqr4gupzqkmm302xww6uyne99rnhl5kjj53wthjypm2qaem9uz9fdf3hzcf0qyghwumn8ghj7ar9dejhstnrdpshgtcq9p382emxd9uz6en0d3kx7am4wqkkjmn2v43hg6t0dckhzat9w4jj6cmvv4shy6twvullqw7x
|
|
139
|
+
const delegationStore = ConversationStore.get(eTag);
|
|
140
|
+
if (delegationStore) {
|
|
141
|
+
try {
|
|
142
|
+
await ConversationStore.addEvent(eTag, event);
|
|
143
|
+
span.addEvent("completion_event_added_to_delegation_store", {
|
|
144
|
+
"delegation.conversation_id": shortenConversationId(eTag),
|
|
145
|
+
});
|
|
146
|
+
} catch (addEventError) {
|
|
147
|
+
// Don't throw after recordCompletion has already run.
|
|
148
|
+
// The completion is recorded - only transcript storage failed.
|
|
149
|
+
logger.warn("[handleDelegationCompletion] Failed to add completion event to delegation store", {
|
|
150
|
+
delegationEventId: eTag.substring(0, 8),
|
|
151
|
+
completionEventId: event.id?.substring(0, 8),
|
|
152
|
+
error: addEventError instanceof Error ? addEventError.message : String(addEventError),
|
|
153
|
+
});
|
|
154
|
+
span.addEvent("completion_event_add_failed", {
|
|
155
|
+
"delegation.conversation_id": shortenConversationId(eTag),
|
|
156
|
+
"error": addEventError instanceof Error ? addEventError.message : String(addEventError),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
break; // Found a matching delegation
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!location) {
|
|
166
|
+
span.addEvent("no_waiting_ral", {
|
|
167
|
+
"delegation.etags_checked": eTags.length,
|
|
168
|
+
"delegation.first_etag": eTags[0],
|
|
169
|
+
});
|
|
170
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
171
|
+
return { recorded: false };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
span.setAttribute("delegation.event_id", delegationEventId || "unknown");
|
|
175
|
+
|
|
176
|
+
// Get the target agent for logging
|
|
177
|
+
const projectCtx = getProjectContext();
|
|
178
|
+
|
|
179
|
+
// Note: We don't spawn an execution here - AgentDispatchService handles that
|
|
180
|
+
// via delegationTarget detection. This handler just records the completion.
|
|
181
|
+
|
|
182
|
+
// Get counts from conversation storage
|
|
183
|
+
const pendingDelegations = ralRegistry.getConversationPendingDelegations(
|
|
184
|
+
location.agentPubkey, location.conversationId, location.ralNumber
|
|
185
|
+
);
|
|
186
|
+
const completedDelegations = ralRegistry.getConversationCompletedDelegations(
|
|
187
|
+
location.agentPubkey, location.conversationId, location.ralNumber
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const targetAgent = projectCtx.getAgentByPubkey(location.agentPubkey);
|
|
191
|
+
const agentSlug = targetAgent?.slug;
|
|
192
|
+
|
|
193
|
+
span.setAttributes({
|
|
194
|
+
"agent.pubkey": location.agentPubkey,
|
|
195
|
+
"agent.slug": agentSlug || "unknown",
|
|
196
|
+
"conversation.id": shortenConversationId(location.conversationId),
|
|
197
|
+
"delegation.pending_count": pendingDelegations.length,
|
|
198
|
+
"delegation.completed_count": completedDelegations.length,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
span.addEvent("completion_recorded", {
|
|
202
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
203
|
+
"delegation.event_id": delegationEventId!, // Non-null when location is set
|
|
204
|
+
"responder.pubkey": event.pubkey,
|
|
205
|
+
"response.length": event.content?.length || 0,
|
|
206
|
+
});
|
|
207
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
208
|
+
|
|
209
|
+
logger.info("[handleDelegationCompletion] Recorded delegation completion", {
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
211
|
+
delegationEventId: delegationEventId!.substring(0, 8),
|
|
212
|
+
agentSlug,
|
|
213
|
+
conversationId: location.conversationId.substring(0, 8),
|
|
214
|
+
completionEventId: event.id?.substring(0, 8),
|
|
215
|
+
completedCount: completedDelegations.length,
|
|
216
|
+
pendingCount: pendingDelegations.length,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
recorded: true,
|
|
221
|
+
agentSlug,
|
|
222
|
+
conversationId: location.conversationId,
|
|
223
|
+
pendingCount: pendingDelegations.length,
|
|
224
|
+
};
|
|
225
|
+
} catch (error) {
|
|
226
|
+
span.recordException(error as Error);
|
|
227
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
228
|
+
throw error;
|
|
229
|
+
} finally {
|
|
230
|
+
span.end();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { type FeatureExtractionPipeline, type Tensor, pipeline } from "@huggingface/transformers";
|
|
2
|
+
|
|
3
|
+
export interface EmbeddingProvider {
|
|
4
|
+
/**
|
|
5
|
+
* Generate embedding for a single text
|
|
6
|
+
*/
|
|
7
|
+
embed(text: string): Promise<Float32Array>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generate embeddings for multiple texts
|
|
11
|
+
*/
|
|
12
|
+
embedBatch(texts: string[]): Promise<Float32Array[]>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get the dimension of the embeddings
|
|
16
|
+
*/
|
|
17
|
+
getDimensions(): Promise<number>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get model identifier
|
|
21
|
+
*/
|
|
22
|
+
getModelId(): string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Local transformer-based embedding provider using @huggingface/transformers
|
|
27
|
+
*/
|
|
28
|
+
export class LocalTransformerEmbeddingProvider implements EmbeddingProvider {
|
|
29
|
+
private extractorPipeline: FeatureExtractionPipeline | null = null;
|
|
30
|
+
private modelId: string;
|
|
31
|
+
private dimensions: number | null = null;
|
|
32
|
+
private initializationPromise: Promise<void> | null = null;
|
|
33
|
+
|
|
34
|
+
constructor(modelId = "Xenova/all-MiniLM-L6-v2") {
|
|
35
|
+
this.modelId = modelId;
|
|
36
|
+
this.initializationPromise = this.initialize();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Initialize the provider by generating a test embedding to determine dimensions.
|
|
41
|
+
* This ensures dimensions are always available after construction completes.
|
|
42
|
+
*/
|
|
43
|
+
private async initialize(): Promise<void> {
|
|
44
|
+
try {
|
|
45
|
+
const pipe = await this.ensurePipeline();
|
|
46
|
+
const output = await pipe("test", { pooling: "mean", normalize: true });
|
|
47
|
+
const embedding = this.tensorToFloat32Array(output);
|
|
48
|
+
this.dimensions = embedding.length;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Failed to initialize embedding provider: ${
|
|
52
|
+
error instanceof Error ? error.message : String(error)
|
|
53
|
+
}`,
|
|
54
|
+
{ cause: error }
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private tensorToFloat32Array(tensor: Tensor): Float32Array {
|
|
60
|
+
return tensor.data instanceof Float32Array
|
|
61
|
+
? tensor.data
|
|
62
|
+
: new Float32Array(tensor.data as ArrayLike<number>);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private async ensurePipeline(): Promise<FeatureExtractionPipeline> {
|
|
66
|
+
if (!this.extractorPipeline) {
|
|
67
|
+
this.extractorPipeline = await pipeline("feature-extraction", this.modelId);
|
|
68
|
+
}
|
|
69
|
+
return this.extractorPipeline;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public async embed(text: string): Promise<Float32Array> {
|
|
73
|
+
const embeddings = await this.embedBatch([text]);
|
|
74
|
+
return embeddings[0];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public async embedBatch(texts: string[]): Promise<Float32Array[]> {
|
|
78
|
+
await this.ensureInitialized();
|
|
79
|
+
const pipe = await this.ensurePipeline();
|
|
80
|
+
const results: Float32Array[] = [];
|
|
81
|
+
|
|
82
|
+
for (const text of texts) {
|
|
83
|
+
const output = await pipe(text, { pooling: "mean", normalize: true });
|
|
84
|
+
results.push(this.tensorToFloat32Array(output));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return results;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Ensure the provider has completed initialization.
|
|
92
|
+
* This guarantees dimensions are available before any operations.
|
|
93
|
+
*/
|
|
94
|
+
private async ensureInitialized(): Promise<void> {
|
|
95
|
+
if (this.initializationPromise) {
|
|
96
|
+
await this.initializationPromise;
|
|
97
|
+
this.initializationPromise = null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get embedding dimensions.
|
|
103
|
+
* Dimensions are guaranteed to be available after initialization completes.
|
|
104
|
+
*/
|
|
105
|
+
public async getDimensions(): Promise<number> {
|
|
106
|
+
await this.ensureInitialized();
|
|
107
|
+
|
|
108
|
+
if (this.dimensions === null) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
"Embedding dimensions not available after initialization. " +
|
|
111
|
+
"This indicates a critical initialization failure."
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return this.dimensions;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public getModelId(): string {
|
|
119
|
+
return this.modelId;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* OpenAI-compatible embedding provider
|
|
125
|
+
* Works with OpenAI, OpenRouter, and other OpenAI-compatible APIs
|
|
126
|
+
*/
|
|
127
|
+
export class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
128
|
+
private apiKey: string;
|
|
129
|
+
private modelId: string;
|
|
130
|
+
private baseUrl: string;
|
|
131
|
+
private dimensions: number | null = null;
|
|
132
|
+
|
|
133
|
+
constructor(apiKey: string, modelId = "text-embedding-3-small", baseUrl = "https://api.openai.com/v1") {
|
|
134
|
+
this.apiKey = apiKey;
|
|
135
|
+
this.modelId = modelId;
|
|
136
|
+
this.baseUrl = baseUrl.replace(/\/$/, ""); // Remove trailing slash
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public async embed(text: string): Promise<Float32Array> {
|
|
140
|
+
const embeddings = await this.embedBatch([text]);
|
|
141
|
+
return embeddings[0];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public async embedBatch(texts: string[]): Promise<Float32Array[]> {
|
|
145
|
+
const response = await fetch(`${this.baseUrl}/embeddings`, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: {
|
|
148
|
+
"Content-Type": "application/json",
|
|
149
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
150
|
+
},
|
|
151
|
+
body: JSON.stringify({
|
|
152
|
+
model: this.modelId,
|
|
153
|
+
input: texts,
|
|
154
|
+
}),
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
throw new Error(`OpenAI API error: ${response.statusText}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
interface EmbeddingResponse {
|
|
162
|
+
data: Array<{ embedding: number[] }>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const data = (await response.json()) as EmbeddingResponse;
|
|
166
|
+
|
|
167
|
+
const embeddings = data.data.map((item) => new Float32Array(item.embedding));
|
|
168
|
+
|
|
169
|
+
// Cache dimensions from first successful response
|
|
170
|
+
if (this.dimensions === null && embeddings.length > 0) {
|
|
171
|
+
this.dimensions = embeddings[0].length;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return embeddings;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public async getDimensions(): Promise<number> {
|
|
178
|
+
// If we haven't cached dimensions yet, make a test embedding call
|
|
179
|
+
if (this.dimensions === null) {
|
|
180
|
+
await this.embed("test");
|
|
181
|
+
}
|
|
182
|
+
return this.dimensions!;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public getModelId(): string {
|
|
186
|
+
return this.modelId;
|
|
187
|
+
}
|
|
188
|
+
}
|