@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,573 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { URL } from "node:url";
|
|
4
|
+
import type { ToolExecutionContext } from "@/tools/types";
|
|
5
|
+
import { type RAGDocument, RAGService } from "@/services/rag/RAGService";
|
|
6
|
+
import type { AISdkTool } from "@/tools/types";
|
|
7
|
+
import type { DocumentMetadata } from "@/services/rag/rag-utils";
|
|
8
|
+
import { type ToolResponse, executeToolWithErrorHandling, resolveAndValidatePath } from "@/tools/utils";
|
|
9
|
+
import { getProjectContext, isProjectContextInitialized } from "@/services/projects";
|
|
10
|
+
import { tool } from "ai";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
// Protocol Constants
|
|
14
|
+
const PROTOCOL_FILE = "file:";
|
|
15
|
+
const PROTOCOL_HTTP = "http:";
|
|
16
|
+
const PROTOCOL_HTTPS = "https:";
|
|
17
|
+
const FILE_PROTOCOL_PREFIX = "file://";
|
|
18
|
+
|
|
19
|
+
// Size and Timeout Constants
|
|
20
|
+
const HTTP_TIMEOUT_MS = 30000; // 30 seconds
|
|
21
|
+
const MAX_FILE_SIZE_MB = 100; // 100MB max file size
|
|
22
|
+
const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024;
|
|
23
|
+
|
|
24
|
+
// User Agent for HTTP requests
|
|
25
|
+
const HTTP_USER_AGENT = "TENEX-RAG-Ingester/1.0";
|
|
26
|
+
|
|
27
|
+
// Type definitions for protocol handlers
|
|
28
|
+
type ProtocolHandler = (uri: string, context: { workingDirectory: string }) => Promise<string>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Schema for RAG document input
|
|
32
|
+
*/
|
|
33
|
+
const ragAddDocumentsSchema = z.object({
|
|
34
|
+
description: z
|
|
35
|
+
.string()
|
|
36
|
+
.trim()
|
|
37
|
+
.min(1, "Description is required and cannot be empty")
|
|
38
|
+
.describe(
|
|
39
|
+
"REQUIRED: A clear, concise description of why you're adding these documents (5-10 words). Helps provide human-readable context for the operation."
|
|
40
|
+
),
|
|
41
|
+
collection: z.string().describe("Name of the collection to add documents to"),
|
|
42
|
+
documents: z
|
|
43
|
+
.array(
|
|
44
|
+
z.union([
|
|
45
|
+
// Option 1: Content with optional file_path
|
|
46
|
+
z.object({
|
|
47
|
+
content: z.string().nullable().describe("Text content of the document"),
|
|
48
|
+
file_path: z.string().nullable().describe("Path to file to read content from"),
|
|
49
|
+
metadata: z
|
|
50
|
+
.record(z.string(), z.unknown())
|
|
51
|
+
.nullable()
|
|
52
|
+
.describe("Optional metadata for the document"),
|
|
53
|
+
source: z.string().nullable().describe("Source identifier for the document"),
|
|
54
|
+
id: z
|
|
55
|
+
.string()
|
|
56
|
+
.nullable()
|
|
57
|
+
.describe("Optional unique identifier for the document"),
|
|
58
|
+
}),
|
|
59
|
+
// Option 2: URI-based
|
|
60
|
+
z.object({
|
|
61
|
+
uri: z.string().describe("URI to fetch content from (file://, https://, etc.)"),
|
|
62
|
+
metadata: z
|
|
63
|
+
.record(z.string(), z.unknown())
|
|
64
|
+
.nullable()
|
|
65
|
+
.describe("Optional metadata for the document"),
|
|
66
|
+
source: z.string().nullable().describe("Source identifier for the document"),
|
|
67
|
+
id: z
|
|
68
|
+
.string()
|
|
69
|
+
.nullable()
|
|
70
|
+
.describe("Optional unique identifier for the document"),
|
|
71
|
+
}),
|
|
72
|
+
])
|
|
73
|
+
)
|
|
74
|
+
.describe("Array of documents to add to the collection"),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validate URI format and return parsed URL
|
|
79
|
+
*
|
|
80
|
+
* Rationale: Early validation prevents downstream errors and provides clear feedback
|
|
81
|
+
* about malformed URIs before any network or file operations are attempted.
|
|
82
|
+
*/
|
|
83
|
+
function validateURI(uri: string): URL {
|
|
84
|
+
try {
|
|
85
|
+
return new URL(uri);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Invalid URI format '${uri}': ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
89
|
+
{ cause: error }
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Validate content is not empty
|
|
96
|
+
*
|
|
97
|
+
* Rationale: Empty documents cannot be meaningfully embedded or searched,
|
|
98
|
+
* so we reject them early with a clear error message.
|
|
99
|
+
*/
|
|
100
|
+
function validateContent(content: string, source: string): void {
|
|
101
|
+
if (!content || content.trim().length === 0) {
|
|
102
|
+
throw new Error(`Document from ${source} must have non-empty content`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check if a size exceeds the maximum allowed
|
|
108
|
+
*
|
|
109
|
+
* Rationale: Large files can cause memory issues and slow processing.
|
|
110
|
+
* This validation ensures system stability and predictable performance.
|
|
111
|
+
*/
|
|
112
|
+
function validateSize(sizeInBytes: number, sourceName: string): void {
|
|
113
|
+
if (sizeInBytes > MAX_FILE_SIZE_BYTES) {
|
|
114
|
+
const sizeMB = (sizeInBytes / 1024 / 1024).toFixed(2);
|
|
115
|
+
throw new Error(
|
|
116
|
+
`${sourceName} size (${sizeMB}MB) exceeds maximum allowed size of ${MAX_FILE_SIZE_MB}MB`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Handle common fetch errors with context
|
|
123
|
+
*
|
|
124
|
+
* Rationale: Consistent error messages across protocol handlers
|
|
125
|
+
* make debugging easier and provide better user experience.
|
|
126
|
+
*/
|
|
127
|
+
function handleFetchError(error: unknown, protocol: string): never {
|
|
128
|
+
if (error && typeof error === "object" && "name" in error && error.name === "AbortError") {
|
|
129
|
+
throw new Error(`Request timeout after ${HTTP_TIMEOUT_MS / 1000} seconds`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
133
|
+
throw new Error(`Failed to fetch from ${protocol} URI: ${message}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Type for document input - represents the union of possible document shapes
|
|
138
|
+
*/
|
|
139
|
+
type DocumentInput = {
|
|
140
|
+
content?: string;
|
|
141
|
+
file_path?: string;
|
|
142
|
+
uri?: string;
|
|
143
|
+
metadata?: Record<string, unknown>;
|
|
144
|
+
source?: string;
|
|
145
|
+
id?: string;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Generate consistent source field based on input type
|
|
150
|
+
*
|
|
151
|
+
* Rationale: Consistent source identification helps with debugging
|
|
152
|
+
* and tracking document origins across different input methods.
|
|
153
|
+
*/
|
|
154
|
+
function generateSourceField(doc: DocumentInput, resolvedPath?: string): string | undefined {
|
|
155
|
+
// Explicit source takes precedence
|
|
156
|
+
if (doc.source) {
|
|
157
|
+
return doc.source;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// URI-based document
|
|
161
|
+
if ("uri" in doc && doc.uri) {
|
|
162
|
+
return doc.uri;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// File path-based document
|
|
166
|
+
if (doc.file_path) {
|
|
167
|
+
return `${FILE_PROTOCOL_PREFIX}${resolvedPath || doc.file_path}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Direct content has no default source
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Parse file path from file:// URI
|
|
176
|
+
*
|
|
177
|
+
* Rationale: File URIs have various formats that need normalization
|
|
178
|
+
* for cross-platform compatibility.
|
|
179
|
+
*/
|
|
180
|
+
function parseFilePathFromURI(uri: string): string {
|
|
181
|
+
// Remove 'file://' prefix
|
|
182
|
+
let filePath = uri.substring(FILE_PROTOCOL_PREFIX.length);
|
|
183
|
+
|
|
184
|
+
// Handle different file:// formats
|
|
185
|
+
if (filePath.startsWith("//")) {
|
|
186
|
+
// file:////absolute/path or file://host/path
|
|
187
|
+
filePath = filePath.substring(2);
|
|
188
|
+
} else if (filePath.startsWith("/./")) {
|
|
189
|
+
// file://./relative/path - explicit relative
|
|
190
|
+
filePath = filePath.substring(3);
|
|
191
|
+
} else if (filePath.startsWith("./")) {
|
|
192
|
+
// file://./relative/path - explicit relative
|
|
193
|
+
filePath = filePath.substring(2);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// On Windows, file:///C:/path becomes /C:/path, need to remove leading slash
|
|
197
|
+
if (process.platform === "win32" && filePath.match(/^\/[a-zA-Z]:[\\/]/)) {
|
|
198
|
+
filePath = filePath.slice(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return filePath;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Handle file:// protocol URIs
|
|
206
|
+
*
|
|
207
|
+
* Rationale: File URIs require special handling for path resolution,
|
|
208
|
+
* size validation, and cross-platform compatibility.
|
|
209
|
+
*/
|
|
210
|
+
async function handleFileProtocolURI(
|
|
211
|
+
uri: string,
|
|
212
|
+
context: { workingDirectory: string }
|
|
213
|
+
): Promise<string> {
|
|
214
|
+
try {
|
|
215
|
+
const filePath = parseFilePathFromURI(uri);
|
|
216
|
+
|
|
217
|
+
// Resolve the path (handles both absolute and relative paths)
|
|
218
|
+
const resolvedPath = path.resolve(context.workingDirectory, filePath);
|
|
219
|
+
|
|
220
|
+
// Check file size before reading
|
|
221
|
+
const stats = await stat(resolvedPath);
|
|
222
|
+
validateSize(stats.size, "File");
|
|
223
|
+
|
|
224
|
+
return await readFile(resolvedPath, "utf-8");
|
|
225
|
+
} catch (error) {
|
|
226
|
+
handleFetchError(error, PROTOCOL_FILE);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Handle HTTP/HTTPS protocol URIs with timeout and size limits
|
|
232
|
+
*
|
|
233
|
+
* Rationale: HTTP requests need timeouts to prevent hanging,
|
|
234
|
+
* and size limits to prevent memory exhaustion from large responses.
|
|
235
|
+
*/
|
|
236
|
+
async function handleHttpProtocolURI(
|
|
237
|
+
uri: string,
|
|
238
|
+
_context: { workingDirectory: string }
|
|
239
|
+
): Promise<string> {
|
|
240
|
+
const controller = new AbortController();
|
|
241
|
+
const timeout = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const response = await fetch(uri, {
|
|
245
|
+
signal: controller.signal,
|
|
246
|
+
headers: {
|
|
247
|
+
"User-Agent": HTTP_USER_AGENT,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
if (!response.ok) {
|
|
252
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Check content length before reading body
|
|
256
|
+
const contentLength = response.headers.get("content-length");
|
|
257
|
+
if (contentLength) {
|
|
258
|
+
const sizeInBytes = Number.parseInt(contentLength, 10);
|
|
259
|
+
validateSize(sizeInBytes, "Response");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Read response body with streaming size check
|
|
263
|
+
const chunks: string[] = [];
|
|
264
|
+
let totalSize = 0;
|
|
265
|
+
const reader = response.body?.getReader();
|
|
266
|
+
const decoder = new TextDecoder();
|
|
267
|
+
|
|
268
|
+
if (!reader) {
|
|
269
|
+
throw new Error("Response body is not readable");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
while (true) {
|
|
273
|
+
const { done, value } = await reader.read();
|
|
274
|
+
if (done) break;
|
|
275
|
+
|
|
276
|
+
totalSize += value.length;
|
|
277
|
+
validateSize(totalSize, "Response");
|
|
278
|
+
|
|
279
|
+
chunks.push(decoder.decode(value, { stream: true }));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return chunks.join("");
|
|
283
|
+
} catch (error) {
|
|
284
|
+
handleFetchError(error, uri.startsWith(PROTOCOL_HTTPS) ? PROTOCOL_HTTPS : PROTOCOL_HTTP);
|
|
285
|
+
} finally {
|
|
286
|
+
clearTimeout(timeout);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Extensible protocol handler map
|
|
292
|
+
*/
|
|
293
|
+
const PROTOCOL_HANDLERS: Map<string, ProtocolHandler> = new Map([
|
|
294
|
+
[PROTOCOL_FILE, handleFileProtocolURI],
|
|
295
|
+
[PROTOCOL_HTTP, handleHttpProtocolURI],
|
|
296
|
+
[PROTOCOL_HTTPS, handleHttpProtocolURI],
|
|
297
|
+
]);
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Register a new protocol handler
|
|
301
|
+
*
|
|
302
|
+
* Rationale: Extensibility allows adding support for new protocols
|
|
303
|
+
* without modifying the core implementation.
|
|
304
|
+
*/
|
|
305
|
+
export function registerProtocolHandler(protocol: string, handler: ProtocolHandler): void {
|
|
306
|
+
PROTOCOL_HANDLERS.set(protocol, handler);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Fetch content from a URI using the appropriate protocol handler
|
|
311
|
+
*/
|
|
312
|
+
async function fetchFromURI(uri: string, workingDirectory: string): Promise<string> {
|
|
313
|
+
// Validate URI first
|
|
314
|
+
const parsedUrl = validateURI(uri);
|
|
315
|
+
|
|
316
|
+
// Get protocol handler
|
|
317
|
+
const handler = PROTOCOL_HANDLERS.get(parsedUrl.protocol);
|
|
318
|
+
if (!handler) {
|
|
319
|
+
throw new Error(
|
|
320
|
+
`Unsupported URI protocol '${parsedUrl.protocol}'. ` +
|
|
321
|
+
`Supported protocols: ${Array.from(PROTOCOL_HANDLERS.keys()).join(", ")}`
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Execute handler
|
|
326
|
+
return await handler(uri, { workingDirectory });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Extract document content from various sources (renamed from processSingleDocument)
|
|
331
|
+
*
|
|
332
|
+
* Rationale: This function unifies content extraction from different input types,
|
|
333
|
+
* providing a consistent interface for document processing.
|
|
334
|
+
*/
|
|
335
|
+
async function extractDocumentContentFromSource(
|
|
336
|
+
doc: DocumentInput,
|
|
337
|
+
workingDirectory: string
|
|
338
|
+
): Promise<{ content: string; source: string | undefined }> {
|
|
339
|
+
let content: string;
|
|
340
|
+
let resolvedPath: string | undefined;
|
|
341
|
+
|
|
342
|
+
if ("uri" in doc && doc.uri) {
|
|
343
|
+
// URI-based document
|
|
344
|
+
content = await fetchFromURI(doc.uri, workingDirectory);
|
|
345
|
+
} else {
|
|
346
|
+
// Content/file_path based document
|
|
347
|
+
content = doc.content || "";
|
|
348
|
+
|
|
349
|
+
// Read from file if file_path is provided
|
|
350
|
+
if (doc.file_path) {
|
|
351
|
+
resolvedPath = resolveAndValidatePath(doc.file_path, workingDirectory);
|
|
352
|
+
|
|
353
|
+
// Check file size
|
|
354
|
+
const stats = await stat(resolvedPath);
|
|
355
|
+
validateSize(stats.size, "File");
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
content = await readFile(resolvedPath, "utf-8");
|
|
359
|
+
} catch (error) {
|
|
360
|
+
if (!doc.content) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
`Cannot read file '${doc.file_path}': ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
363
|
+
{ cause: error }
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
// Fall back to provided content if file read fails but content exists
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Generate source field
|
|
372
|
+
const source = generateSourceField(doc, resolvedPath);
|
|
373
|
+
|
|
374
|
+
return { content, source };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Compute base provenance metadata once per tool execution
|
|
379
|
+
*
|
|
380
|
+
* Rationale: Pre-computing provenance avoids repeated global context access
|
|
381
|
+
* per document. This is called once in executeAddDocuments and passed down.
|
|
382
|
+
*/
|
|
383
|
+
function computeBaseProvenance(context: ToolExecutionContext): DocumentMetadata {
|
|
384
|
+
const provenance: DocumentMetadata = {};
|
|
385
|
+
|
|
386
|
+
// Auto-inject agent_pubkey if available
|
|
387
|
+
if (context.agent?.pubkey) {
|
|
388
|
+
provenance.agent_pubkey = context.agent.pubkey;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Auto-inject project_id if available (uses NIP-33 address format)
|
|
392
|
+
if (isProjectContextInitialized()) {
|
|
393
|
+
try {
|
|
394
|
+
const projectCtx = getProjectContext();
|
|
395
|
+
const projectId = projectCtx.project.tagId();
|
|
396
|
+
if (projectId) {
|
|
397
|
+
provenance.project_id = projectId;
|
|
398
|
+
}
|
|
399
|
+
} catch {
|
|
400
|
+
// Project context not available - skip project_id injection
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return provenance;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Coerce unknown record values to JSON-serializable DocumentMetadata values.
|
|
409
|
+
* Filters out non-serializable values (functions, undefined, symbols) and
|
|
410
|
+
* coerces compatible values to their JSON equivalents.
|
|
411
|
+
*
|
|
412
|
+
* Rationale: User-provided metadata from tool input may contain unknown types.
|
|
413
|
+
* Instead of blindly casting, we validate/coerce each value to ensure type safety.
|
|
414
|
+
*/
|
|
415
|
+
function coerceToDocumentMetadata(record: Record<string, unknown>): DocumentMetadata {
|
|
416
|
+
const result: DocumentMetadata = {};
|
|
417
|
+
|
|
418
|
+
for (const [key, value] of Object.entries(record)) {
|
|
419
|
+
// Skip non-serializable values
|
|
420
|
+
if (value === undefined || typeof value === "function" || typeof value === "symbol") {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Handle primitives directly
|
|
425
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
426
|
+
result[key] = value;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Handle arrays and objects by JSON round-trip (ensures serializable)
|
|
431
|
+
if (typeof value === "object") {
|
|
432
|
+
try {
|
|
433
|
+
// JSON round-trip validates serializability and clones deeply
|
|
434
|
+
result[key] = JSON.parse(JSON.stringify(value));
|
|
435
|
+
} catch {
|
|
436
|
+
// Non-serializable object (circular refs, etc.) - skip
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return result;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Merge document metadata with base provenance
|
|
447
|
+
*
|
|
448
|
+
* Rationale: Separates provenance computation (done once) from per-document
|
|
449
|
+
* metadata merging. Caller-provided metadata takes precedence over auto-injected.
|
|
450
|
+
*/
|
|
451
|
+
function mergeWithProvenance(
|
|
452
|
+
documentMetadata: Record<string, unknown> | null | undefined,
|
|
453
|
+
baseProvenance: DocumentMetadata
|
|
454
|
+
): DocumentMetadata {
|
|
455
|
+
// Coerce user-provided metadata to ensure JSON-serializable values
|
|
456
|
+
const coercedMetadata = documentMetadata ? coerceToDocumentMetadata(documentMetadata) : {};
|
|
457
|
+
|
|
458
|
+
// Base provenance (agent_pubkey, project_id) comes first, user metadata can override
|
|
459
|
+
return {
|
|
460
|
+
...baseProvenance,
|
|
461
|
+
...coercedMetadata,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Process documents and prepare them for insertion
|
|
467
|
+
*/
|
|
468
|
+
async function processDocuments(
|
|
469
|
+
documents: z.infer<typeof ragAddDocumentsSchema>["documents"],
|
|
470
|
+
workingDirectory: string,
|
|
471
|
+
baseProvenance: DocumentMetadata
|
|
472
|
+
): Promise<RAGDocument[]> {
|
|
473
|
+
const processedDocs: RAGDocument[] = [];
|
|
474
|
+
|
|
475
|
+
for (const doc of documents) {
|
|
476
|
+
try {
|
|
477
|
+
const { content, source } = await extractDocumentContentFromSource(
|
|
478
|
+
doc as DocumentInput,
|
|
479
|
+
workingDirectory
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
// Validate content
|
|
483
|
+
validateContent(content, source || "document");
|
|
484
|
+
|
|
485
|
+
// Merge document metadata with pre-computed provenance
|
|
486
|
+
const metadata = mergeWithProvenance(doc.metadata, baseProvenance);
|
|
487
|
+
|
|
488
|
+
processedDocs.push({
|
|
489
|
+
id: doc.id ?? undefined,
|
|
490
|
+
content,
|
|
491
|
+
metadata,
|
|
492
|
+
source: source ?? undefined,
|
|
493
|
+
timestamp: Date.now(),
|
|
494
|
+
});
|
|
495
|
+
} catch (error) {
|
|
496
|
+
// Add context to error
|
|
497
|
+
const identifier =
|
|
498
|
+
doc.id || ("uri" in doc ? doc.uri : doc.file_path) || "unknown document";
|
|
499
|
+
throw new Error(
|
|
500
|
+
`Error processing document '${identifier}': ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
501
|
+
{ cause: error }
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return processedDocs;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Core implementation of adding documents to a RAG collection
|
|
511
|
+
*/
|
|
512
|
+
async function executeAddDocuments(
|
|
513
|
+
input: z.infer<typeof ragAddDocumentsSchema>,
|
|
514
|
+
context: ToolExecutionContext
|
|
515
|
+
): Promise<ToolResponse> {
|
|
516
|
+
const { collection, documents } = input;
|
|
517
|
+
|
|
518
|
+
// Pre-compute base provenance once (avoids repeated global context access per document)
|
|
519
|
+
const baseProvenance = computeBaseProvenance(context);
|
|
520
|
+
|
|
521
|
+
// Process documents with provenance injection
|
|
522
|
+
const processedDocs = await processDocuments(documents, context.workingDirectory, baseProvenance);
|
|
523
|
+
|
|
524
|
+
// Add to collection
|
|
525
|
+
const ragService = RAGService.getInstance();
|
|
526
|
+
await ragService.addDocuments(collection, processedDocs);
|
|
527
|
+
|
|
528
|
+
return {
|
|
529
|
+
success: true,
|
|
530
|
+
message: `Successfully added ${processedDocs.length} documents to collection '${collection}'`,
|
|
531
|
+
documents_added: processedDocs.length,
|
|
532
|
+
collection: collection,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Add documents to a RAG collection for semantic search
|
|
538
|
+
*
|
|
539
|
+
* Supports multiple input methods:
|
|
540
|
+
* - Direct text content
|
|
541
|
+
* - File paths (relative or absolute)
|
|
542
|
+
* - URIs (file://, http://, https://)
|
|
543
|
+
*
|
|
544
|
+
* Features:
|
|
545
|
+
* - Automatic content validation (non-empty check)
|
|
546
|
+
* - File size limits (100MB) with early validation
|
|
547
|
+
* - HTTP request timeouts (30s) to prevent hanging
|
|
548
|
+
* - Streaming size validation for HTTP responses
|
|
549
|
+
* - Extensible protocol handlers for custom schemes
|
|
550
|
+
* - Cross-platform file path resolution
|
|
551
|
+
* - Backward compatible with existing code
|
|
552
|
+
*
|
|
553
|
+
* Rationale for validations:
|
|
554
|
+
* - Size limits prevent memory exhaustion
|
|
555
|
+
* - Timeouts prevent hanging requests
|
|
556
|
+
* - URI validation provides early error detection
|
|
557
|
+
* - Content validation ensures meaningful documents
|
|
558
|
+
*/
|
|
559
|
+
export function createRAGAddDocumentsTool(context: ToolExecutionContext): AISdkTool {
|
|
560
|
+
return tool({
|
|
561
|
+
description:
|
|
562
|
+
"Add documents to a RAG collection. Documents can be provided as text content, file paths, or URIs (file://, https://, etc.). Each document will be automatically embedded for semantic search. Enforces file size limits (100MB) and HTTP timeouts (30s).",
|
|
563
|
+
inputSchema: ragAddDocumentsSchema,
|
|
564
|
+
execute: async (input: unknown) => {
|
|
565
|
+
return executeToolWithErrorHandling(
|
|
566
|
+
"rag_add_documents",
|
|
567
|
+
input as z.infer<typeof ragAddDocumentsSchema>,
|
|
568
|
+
context,
|
|
569
|
+
executeAddDocuments
|
|
570
|
+
);
|
|
571
|
+
},
|
|
572
|
+
}) as AISdkTool;
|
|
573
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ToolExecutionContext } from "@/tools/types";
|
|
2
|
+
import { RAGService } from "@/services/rag/RAGService";
|
|
3
|
+
import type { AISdkTool } from "@/tools/types";
|
|
4
|
+
import { type ToolResponse, executeToolWithErrorHandling } from "@/tools/utils";
|
|
5
|
+
import { tool } from "ai";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
const ragCreateCollectionSchema = z.object({
|
|
9
|
+
description: z
|
|
10
|
+
.string()
|
|
11
|
+
.trim()
|
|
12
|
+
.min(1, "Description is required and cannot be empty")
|
|
13
|
+
.describe(
|
|
14
|
+
"REQUIRED: A clear, concise description of why you're creating this collection (5-10 words). Helps provide human-readable context for the operation."
|
|
15
|
+
),
|
|
16
|
+
name: z.string().describe("Name of the collection to create (alphanumeric with underscores)"),
|
|
17
|
+
schema: z
|
|
18
|
+
.record(z.string(), z.any())
|
|
19
|
+
.nullable()
|
|
20
|
+
.describe(
|
|
21
|
+
"Optional custom schema for the collection (default includes id, content, vector, metadata, timestamp, source)"
|
|
22
|
+
),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Core implementation of RAG collection creation
|
|
27
|
+
*/
|
|
28
|
+
async function executeCreateCollection(
|
|
29
|
+
input: z.infer<typeof ragCreateCollectionSchema>,
|
|
30
|
+
_context: ToolExecutionContext
|
|
31
|
+
): Promise<ToolResponse> {
|
|
32
|
+
const { name, schema } = input;
|
|
33
|
+
|
|
34
|
+
const ragService = RAGService.getInstance();
|
|
35
|
+
const collection = await ragService.createCollection(name, schema ?? undefined);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
success: true,
|
|
39
|
+
message: `Collection '${name}' created successfully`,
|
|
40
|
+
collection: {
|
|
41
|
+
name: collection.name,
|
|
42
|
+
created_at: new Date(collection.created_at).toISOString(),
|
|
43
|
+
schema: collection.schema,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Create a new RAG collection for storing and retrieving vector embeddings
|
|
50
|
+
*/
|
|
51
|
+
export function createRAGCreateCollectionTool(context: ToolExecutionContext): AISdkTool {
|
|
52
|
+
return tool({
|
|
53
|
+
description:
|
|
54
|
+
"Create a new RAG collection (vector database) for storing documents with semantic search capabilities",
|
|
55
|
+
inputSchema: ragCreateCollectionSchema,
|
|
56
|
+
execute: async (input: unknown) => {
|
|
57
|
+
return executeToolWithErrorHandling(
|
|
58
|
+
"rag_create_collection",
|
|
59
|
+
input as z.infer<typeof ragCreateCollectionSchema>,
|
|
60
|
+
context,
|
|
61
|
+
executeCreateCollection
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
}) as AISdkTool;
|
|
65
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ToolExecutionContext } from "@/tools/types";
|
|
2
|
+
import { RAGService } from "@/services/rag/RAGService";
|
|
3
|
+
import type { AISdkTool } from "@/tools/types";
|
|
4
|
+
import { type ToolResponse, executeToolWithErrorHandling } from "@/tools/utils";
|
|
5
|
+
import { tool } from "ai";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
const ragDeleteCollectionSchema = z.object({
|
|
9
|
+
description: z
|
|
10
|
+
.string()
|
|
11
|
+
.trim()
|
|
12
|
+
.min(1, "Description is required and cannot be empty")
|
|
13
|
+
.describe(
|
|
14
|
+
"REQUIRED: A clear, concise description of why you're deleting this collection (5-10 words). Helps provide human-readable context for the operation."
|
|
15
|
+
),
|
|
16
|
+
name: z.string().describe("Name of the collection to delete"),
|
|
17
|
+
confirm: z
|
|
18
|
+
.boolean()
|
|
19
|
+
.nullable()
|
|
20
|
+
.default(false)
|
|
21
|
+
.describe("Confirmation flag to prevent accidental deletion (must be true to proceed)"),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Core implementation of RAG collection deletion
|
|
26
|
+
*/
|
|
27
|
+
async function executeDeleteCollection(
|
|
28
|
+
input: z.infer<typeof ragDeleteCollectionSchema>,
|
|
29
|
+
_context: ToolExecutionContext
|
|
30
|
+
): Promise<ToolResponse> {
|
|
31
|
+
const { name, confirm = false } = input;
|
|
32
|
+
|
|
33
|
+
if (!confirm) {
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
error: "Deletion requires confirmation. Set confirm=true to proceed.",
|
|
37
|
+
warning: `This will permanently delete the collection '${name}' and all its documents.`,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ragService = RAGService.getInstance();
|
|
42
|
+
await ragService.deleteCollection(name);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
message: `Collection '${name}' has been permanently deleted`,
|
|
47
|
+
deleted_collection: name,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Delete a RAG collection and all its documents
|
|
53
|
+
*/
|
|
54
|
+
export function createRAGDeleteCollectionTool(context: ToolExecutionContext): AISdkTool {
|
|
55
|
+
return tool({
|
|
56
|
+
description:
|
|
57
|
+
"Delete a RAG collection and all its documents. This action is permanent and requires confirmation.",
|
|
58
|
+
inputSchema: ragDeleteCollectionSchema,
|
|
59
|
+
execute: async (input: unknown) => {
|
|
60
|
+
return executeToolWithErrorHandling(
|
|
61
|
+
"rag_delete_collection",
|
|
62
|
+
input as z.infer<typeof ragDeleteCollectionSchema>,
|
|
63
|
+
context,
|
|
64
|
+
executeDeleteCollection
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
}) as AISdkTool;
|
|
68
|
+
}
|