@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,224 @@
|
|
|
1
|
+
import { getNDK } from "@/nostr";
|
|
2
|
+
import { TagExtractor } from "@/nostr/TagExtractor";
|
|
3
|
+
import { logger } from "@/utils/logger";
|
|
4
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
5
|
+
import { SpanStatusCode, context as otelContext, trace } from "@opentelemetry/api";
|
|
6
|
+
import type { NudgeResult, NudgeToolPermissions, NudgeData } from "./types";
|
|
7
|
+
|
|
8
|
+
const tracer = trace.getTracer("tenex.nudge-service");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Service for fetching and processing Agent Nudge events (kind:4201)
|
|
12
|
+
* Single Responsibility: Retrieve nudge content and concatenate for system prompt injection
|
|
13
|
+
*/
|
|
14
|
+
export class NudgeService {
|
|
15
|
+
private static instance: NudgeService;
|
|
16
|
+
|
|
17
|
+
private constructor() {}
|
|
18
|
+
|
|
19
|
+
static getInstance(): NudgeService {
|
|
20
|
+
if (!NudgeService.instance) {
|
|
21
|
+
NudgeService.instance = new NudgeService();
|
|
22
|
+
}
|
|
23
|
+
return NudgeService.instance;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Fetch nudge events by IDs and concatenate their content
|
|
28
|
+
* @param eventIds Array of nudge event IDs to fetch
|
|
29
|
+
* @returns Concatenated content from all nudges, or empty string if none found
|
|
30
|
+
*/
|
|
31
|
+
async fetchNudges(eventIds: string[]): Promise<string> {
|
|
32
|
+
if (eventIds.length === 0) {
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const span = tracer.startSpan("tenex.nudge.fetch_nudges", {
|
|
37
|
+
attributes: {
|
|
38
|
+
"nudge.requested_count": eventIds.length,
|
|
39
|
+
},
|
|
40
|
+
}, otelContext.active());
|
|
41
|
+
|
|
42
|
+
return otelContext.with(trace.setSpan(otelContext.active(), span), async () => {
|
|
43
|
+
try {
|
|
44
|
+
const ndk = getNDK();
|
|
45
|
+
const nudgeEvents = await ndk.fetchEvents({
|
|
46
|
+
ids: eventIds,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const nudges = Array.from(nudgeEvents);
|
|
50
|
+
const concatenated = nudges
|
|
51
|
+
.map((nudge) => nudge.content.trim())
|
|
52
|
+
.filter((content) => content.length > 0)
|
|
53
|
+
.join("\n\n");
|
|
54
|
+
|
|
55
|
+
const nudgeTitles = nudges.map((n) => n.tagValue("title") || "untitled").join(", ");
|
|
56
|
+
|
|
57
|
+
span.setAttributes({
|
|
58
|
+
"nudge.fetched_count": nudges.length,
|
|
59
|
+
"nudge.content_length": concatenated.length,
|
|
60
|
+
"nudge.titles": nudgeTitles,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
64
|
+
span.end();
|
|
65
|
+
return concatenated;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
span.recordException(error as Error);
|
|
68
|
+
span.setStatus({
|
|
69
|
+
code: SpanStatusCode.ERROR,
|
|
70
|
+
message: (error as Error).message,
|
|
71
|
+
});
|
|
72
|
+
span.end();
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Fetch a single nudge event by ID
|
|
80
|
+
* @param eventId The nudge event ID
|
|
81
|
+
* @returns The nudge event or null if not found
|
|
82
|
+
*/
|
|
83
|
+
async fetchNudge(eventId: string): Promise<NDKEvent | null> {
|
|
84
|
+
try {
|
|
85
|
+
const ndk = getNDK();
|
|
86
|
+
const events = await ndk.fetchEvents({
|
|
87
|
+
ids: [eventId],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const nudge = Array.from(events).find((event) => event.kind === 4201);
|
|
91
|
+
return nudge || null;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
logger.error("[NudgeService] Failed to fetch nudge", { error, eventId });
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Fetch nudge events and extract both content and tool permissions.
|
|
100
|
+
* This method extracts:
|
|
101
|
+
* - only-tool tags: Highest priority, replaces ALL tools
|
|
102
|
+
* - allow-tool tags: Adds tools to agent's default set
|
|
103
|
+
* - deny-tool tags: Removes tools from agent's default set
|
|
104
|
+
*
|
|
105
|
+
* @param eventIds Array of nudge event IDs to fetch
|
|
106
|
+
* @returns NudgeResult with content and tool permissions
|
|
107
|
+
*/
|
|
108
|
+
async fetchNudgesWithPermissions(eventIds: string[]): Promise<NudgeResult> {
|
|
109
|
+
const emptyResult: NudgeResult = {
|
|
110
|
+
nudges: [],
|
|
111
|
+
content: "",
|
|
112
|
+
toolPermissions: {},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if (eventIds.length === 0) {
|
|
116
|
+
return emptyResult;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const span = tracer.startSpan("tenex.nudge.fetch_nudges_with_permissions", {
|
|
120
|
+
attributes: {
|
|
121
|
+
"nudge.requested_count": eventIds.length,
|
|
122
|
+
},
|
|
123
|
+
}, otelContext.active());
|
|
124
|
+
|
|
125
|
+
return otelContext.with(trace.setSpan(otelContext.active(), span), async () => {
|
|
126
|
+
try {
|
|
127
|
+
const ndk = getNDK();
|
|
128
|
+
const nudgeEvents = await ndk.fetchEvents({
|
|
129
|
+
ids: eventIds,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const nudges = Array.from(nudgeEvents);
|
|
133
|
+
|
|
134
|
+
// Build nudge data array
|
|
135
|
+
const nudgeDataArray: NudgeData[] = nudges
|
|
136
|
+
.map((nudge) => ({
|
|
137
|
+
content: nudge.content.trim(),
|
|
138
|
+
title: nudge.tagValue("title") || undefined,
|
|
139
|
+
}))
|
|
140
|
+
.filter((data) => data.content.length > 0);
|
|
141
|
+
|
|
142
|
+
// Concatenate content for backward compatibility
|
|
143
|
+
const concatenated = nudgeDataArray
|
|
144
|
+
.map((data) => data.content)
|
|
145
|
+
.join("\n\n");
|
|
146
|
+
|
|
147
|
+
// Extract tool permissions from all nudges
|
|
148
|
+
const toolPermissions = this.extractToolPermissions(nudges);
|
|
149
|
+
|
|
150
|
+
const nudgeTitles = nudges.map((n) => n.tagValue("title") || "untitled").join(", ");
|
|
151
|
+
|
|
152
|
+
span.setAttributes({
|
|
153
|
+
"nudge.fetched_count": nudges.length,
|
|
154
|
+
"nudge.content_length": concatenated.length,
|
|
155
|
+
"nudge.titles": nudgeTitles,
|
|
156
|
+
"nudge.only_tools_count": toolPermissions.onlyTools?.length ?? 0,
|
|
157
|
+
"nudge.allow_tools_count": toolPermissions.allowTools?.length ?? 0,
|
|
158
|
+
"nudge.deny_tools_count": toolPermissions.denyTools?.length ?? 0,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
162
|
+
span.end();
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
nudges: nudgeDataArray,
|
|
166
|
+
content: concatenated,
|
|
167
|
+
toolPermissions,
|
|
168
|
+
};
|
|
169
|
+
} catch (error) {
|
|
170
|
+
span.recordException(error as Error);
|
|
171
|
+
span.setStatus({
|
|
172
|
+
code: SpanStatusCode.ERROR,
|
|
173
|
+
message: (error as Error).message,
|
|
174
|
+
});
|
|
175
|
+
span.end();
|
|
176
|
+
logger.error("[NudgeService] Failed to fetch nudges with permissions", { error });
|
|
177
|
+
return emptyResult;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Extract tool permissions from nudge events.
|
|
184
|
+
* Collects all only-tool, allow-tool, and deny-tool tags across all nudges.
|
|
185
|
+
*
|
|
186
|
+
* @param nudges Array of nudge events
|
|
187
|
+
* @returns Aggregated tool permissions
|
|
188
|
+
*/
|
|
189
|
+
private extractToolPermissions(nudges: NDKEvent[]): NudgeToolPermissions {
|
|
190
|
+
const permissions: NudgeToolPermissions = {};
|
|
191
|
+
|
|
192
|
+
const onlyTools: string[] = [];
|
|
193
|
+
const allowTools: string[] = [];
|
|
194
|
+
const denyTools: string[] = [];
|
|
195
|
+
|
|
196
|
+
for (const nudge of nudges) {
|
|
197
|
+
// Extract only-tool tags
|
|
198
|
+
const onlyToolValues = TagExtractor.getTagValues(nudge, "only-tool");
|
|
199
|
+
onlyTools.push(...onlyToolValues);
|
|
200
|
+
|
|
201
|
+
// Extract allow-tool tags
|
|
202
|
+
const allowToolValues = TagExtractor.getTagValues(nudge, "allow-tool");
|
|
203
|
+
allowTools.push(...allowToolValues);
|
|
204
|
+
|
|
205
|
+
// Extract deny-tool tags
|
|
206
|
+
const denyToolValues = TagExtractor.getTagValues(nudge, "deny-tool");
|
|
207
|
+
denyTools.push(...denyToolValues);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Only set arrays if they have values (to keep the object clean)
|
|
211
|
+
if (onlyTools.length > 0) {
|
|
212
|
+
// Deduplicate and set
|
|
213
|
+
permissions.onlyTools = [...new Set(onlyTools)];
|
|
214
|
+
}
|
|
215
|
+
if (allowTools.length > 0) {
|
|
216
|
+
permissions.allowTools = [...new Set(allowTools)];
|
|
217
|
+
}
|
|
218
|
+
if (denyTools.length > 0) {
|
|
219
|
+
permissions.denyTools = [...new Set(denyTools)];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return permissions;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { getNDK } from "@/nostr";
|
|
2
|
+
import { NDKKind } from "@/nostr/kinds";
|
|
3
|
+
import { logger } from "@/utils/logger";
|
|
4
|
+
import type { NDKEvent, NDKSubscription } from "@nostr-dev-kit/ndk";
|
|
5
|
+
import { SpanStatusCode, context as otelContext, trace } from "@opentelemetry/api";
|
|
6
|
+
|
|
7
|
+
const tracer = trace.getTracer("tenex.nudge-whitelist-service");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Categorized whitelist item - either a nudge or a skill
|
|
11
|
+
*/
|
|
12
|
+
export interface WhitelistItem {
|
|
13
|
+
/** The event ID of the whitelisted nudge/skill */
|
|
14
|
+
eventId: string;
|
|
15
|
+
/** The kind of the referenced event (4201 for nudge, 4202 for skill) */
|
|
16
|
+
kind: typeof NDKKind.AgentNudge | typeof NDKKind.AgentSkill;
|
|
17
|
+
/** The name of the nudge/skill (from title tag) */
|
|
18
|
+
name?: string;
|
|
19
|
+
/** Description of the nudge/skill (full content - truncation is done in presentation layer) */
|
|
20
|
+
description?: string;
|
|
21
|
+
/** Pubkeys that have whitelisted this item (multiple whitelist events can reference same item) */
|
|
22
|
+
whitelistedBy: string[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Cached whitelist data with fetch timestamp
|
|
27
|
+
*/
|
|
28
|
+
interface WhitelistCache {
|
|
29
|
+
/** Whitelisted nudges (kind:4201) */
|
|
30
|
+
nudges: WhitelistItem[];
|
|
31
|
+
/** Whitelisted skills (kind:4202) */
|
|
32
|
+
skills: WhitelistItem[];
|
|
33
|
+
/** When this cache was last updated */
|
|
34
|
+
lastUpdated: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const REBUILD_DEBOUNCE_MS = 500;
|
|
38
|
+
const FETCH_TIMEOUT_MS = 10_000;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Service for managing nudge/skill whitelists.
|
|
42
|
+
*
|
|
43
|
+
* This service subscribes to kind:14202 events from whitelisted pubkeys,
|
|
44
|
+
* which are NIP-51-like lists that e-tag nudge (kind:4201) and skill (kind:4202) events.
|
|
45
|
+
*
|
|
46
|
+
* The service maintains a cached list of all whitelisted nudges and skills,
|
|
47
|
+
* categorized by their event kind. Cache is built incrementally as events
|
|
48
|
+
* stream in from the subscription — initialization never blocks on EOSE.
|
|
49
|
+
*/
|
|
50
|
+
export class NudgeSkillWhitelistService {
|
|
51
|
+
private static instance: NudgeSkillWhitelistService;
|
|
52
|
+
private cache: WhitelistCache | null = null;
|
|
53
|
+
private subscription: NDKSubscription | null = null;
|
|
54
|
+
private whitelistPubkeys: Set<string> = new Set();
|
|
55
|
+
private initialized = false;
|
|
56
|
+
|
|
57
|
+
/** Latest kind:14202 event per author pubkey (replaceable semantics) */
|
|
58
|
+
private latestWhitelistEvents: Map<string, NDKEvent> = new Map();
|
|
59
|
+
/** Fetched nudge/skill events by ID — avoids re-fetching */
|
|
60
|
+
private referencedEventCache: Map<string, NDKEvent> = new Map();
|
|
61
|
+
/** Debounce timer for coalescing rapid event bursts */
|
|
62
|
+
private rebuildTimer: ReturnType<typeof setTimeout> | null = null;
|
|
63
|
+
|
|
64
|
+
private constructor() {}
|
|
65
|
+
|
|
66
|
+
static getInstance(): NudgeSkillWhitelistService {
|
|
67
|
+
if (!NudgeSkillWhitelistService.instance) {
|
|
68
|
+
NudgeSkillWhitelistService.instance = new NudgeSkillWhitelistService();
|
|
69
|
+
}
|
|
70
|
+
return NudgeSkillWhitelistService.instance;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Initialize the service with whitelisted pubkeys.
|
|
75
|
+
* Returns immediately — cache starts empty and populates as events stream in.
|
|
76
|
+
*/
|
|
77
|
+
initialize(whitelistPubkeys: string[]): void {
|
|
78
|
+
if (this.initialized && this.pubkeysMatch(whitelistPubkeys)) {
|
|
79
|
+
logger.debug("[NudgeSkillWhitelistService] Already initialized with same pubkeys, skipping");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.whitelistPubkeys = new Set(whitelistPubkeys);
|
|
84
|
+
this.initialized = true;
|
|
85
|
+
this.cache = { nudges: [], skills: [], lastUpdated: Date.now() };
|
|
86
|
+
|
|
87
|
+
this.startSubscription();
|
|
88
|
+
|
|
89
|
+
logger.info("[NudgeSkillWhitelistService] Initialized", {
|
|
90
|
+
pubkeyCount: whitelistPubkeys.length,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if the given pubkeys match the currently configured whitelist
|
|
96
|
+
*/
|
|
97
|
+
private pubkeysMatch(newPubkeys: string[]): boolean {
|
|
98
|
+
if (newPubkeys.length !== this.whitelistPubkeys.size) return false;
|
|
99
|
+
return newPubkeys.every(pk => this.whitelistPubkeys.has(pk));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Start a subscription to kind:14202 events from whitelisted pubkeys.
|
|
104
|
+
* Updates the cache incrementally as events arrive.
|
|
105
|
+
*/
|
|
106
|
+
private startSubscription(): void {
|
|
107
|
+
if (this.subscription) {
|
|
108
|
+
this.subscription.stop();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (this.whitelistPubkeys.size === 0) {
|
|
112
|
+
logger.debug("[NudgeSkillWhitelistService] No whitelisted pubkeys, skipping subscription");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const ndk = getNDK();
|
|
117
|
+
const authors = Array.from(this.whitelistPubkeys);
|
|
118
|
+
|
|
119
|
+
this.subscription = ndk.subscribe(
|
|
120
|
+
{
|
|
121
|
+
kinds: [NDKKind.NudgeSkillWhitelist],
|
|
122
|
+
authors,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
closeOnEose: false,
|
|
126
|
+
onEvent: (event: NDKEvent) => {
|
|
127
|
+
this.handleWhitelistEvent(event);
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
logger.debug("[NudgeSkillWhitelistService] Started subscription", {
|
|
133
|
+
pubkeyCount: authors.length,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Handle an incoming whitelist event. Applies replaceable semantics
|
|
139
|
+
* (only the latest event per author is kept) and schedules a debounced cache rebuild.
|
|
140
|
+
*/
|
|
141
|
+
private handleWhitelistEvent(event: NDKEvent): void {
|
|
142
|
+
const existing = this.latestWhitelistEvents.get(event.pubkey);
|
|
143
|
+
if (existing && existing.created_at !== undefined && event.created_at !== undefined
|
|
144
|
+
&& existing.created_at >= event.created_at) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
logger.debug("[NudgeSkillWhitelistService] Received whitelist event", {
|
|
149
|
+
eventId: event.id?.substring(0, 12),
|
|
150
|
+
author: event.pubkey.substring(0, 8),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
this.latestWhitelistEvents.set(event.pubkey, event);
|
|
154
|
+
this.scheduleRebuild();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Schedule a debounced cache rebuild. Coalesces rapid event bursts
|
|
159
|
+
* (e.g. the initial subscription replay) into a single rebuild.
|
|
160
|
+
*/
|
|
161
|
+
private scheduleRebuild(): void {
|
|
162
|
+
if (this.rebuildTimer) {
|
|
163
|
+
clearTimeout(this.rebuildTimer);
|
|
164
|
+
}
|
|
165
|
+
this.rebuildTimer = setTimeout(() => {
|
|
166
|
+
this.rebuildTimer = null;
|
|
167
|
+
this.rebuildCache().catch(error => {
|
|
168
|
+
logger.error("[NudgeSkillWhitelistService] Failed to rebuild cache", { error });
|
|
169
|
+
});
|
|
170
|
+
}, REBUILD_DEBOUNCE_MS);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Rebuild the cache from all stored whitelist events.
|
|
175
|
+
* Fetches any referenced nudge/skill events not yet in the local cache.
|
|
176
|
+
*/
|
|
177
|
+
private async rebuildCache(): Promise<void> {
|
|
178
|
+
const span = tracer.startSpan("tenex.nudge-whitelist.rebuild", {
|
|
179
|
+
attributes: {
|
|
180
|
+
"whitelist.pubkey_count": this.whitelistPubkeys.size,
|
|
181
|
+
},
|
|
182
|
+
}, otelContext.active());
|
|
183
|
+
|
|
184
|
+
return otelContext.with(trace.setSpan(otelContext.active(), span), async () => {
|
|
185
|
+
try {
|
|
186
|
+
if (this.latestWhitelistEvents.size === 0) {
|
|
187
|
+
this.cache = { nudges: [], skills: [], lastUpdated: Date.now() };
|
|
188
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
189
|
+
span.end();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Collect all e-tagged event IDs and track whitelisters per event
|
|
194
|
+
const eventToWhitelisters: Map<string, Set<string>> = new Map();
|
|
195
|
+
|
|
196
|
+
for (const event of this.latestWhitelistEvents.values()) {
|
|
197
|
+
const eTags = event.tags.filter(tag => tag[0] === "e" && tag[1]);
|
|
198
|
+
for (const eTag of eTags) {
|
|
199
|
+
const eventId = eTag[1];
|
|
200
|
+
if (!eventToWhitelisters.has(eventId)) {
|
|
201
|
+
eventToWhitelisters.set(eventId, new Set());
|
|
202
|
+
}
|
|
203
|
+
eventToWhitelisters.get(eventId)!.add(event.pubkey);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (eventToWhitelisters.size === 0) {
|
|
208
|
+
this.cache = { nudges: [], skills: [], lastUpdated: Date.now() };
|
|
209
|
+
span.setAttributes({ "whitelist.item_count": 0 });
|
|
210
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
211
|
+
span.end();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Find IDs not yet in our local cache
|
|
216
|
+
const unfetchedIds: string[] = [];
|
|
217
|
+
for (const id of eventToWhitelisters.keys()) {
|
|
218
|
+
if (!this.referencedEventCache.has(id)) {
|
|
219
|
+
unfetchedIds.push(id);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Batch-fetch unfetched events with a timeout
|
|
224
|
+
if (unfetchedIds.length > 0) {
|
|
225
|
+
try {
|
|
226
|
+
const ndk = getNDK();
|
|
227
|
+
const fetchPromise = ndk.fetchEvents({ ids: unfetchedIds });
|
|
228
|
+
const timeoutPromise = new Promise<Set<NDKEvent>>((_, reject) =>
|
|
229
|
+
setTimeout(() => reject(new Error("fetchEvents timeout")), FETCH_TIMEOUT_MS)
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const fetched = await Promise.race([fetchPromise, timeoutPromise]);
|
|
233
|
+
for (const event of fetched) {
|
|
234
|
+
this.referencedEventCache.set(event.id, event);
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
logger.warn("[NudgeSkillWhitelistService] Fetch timed out or failed, using cached events", {
|
|
238
|
+
unfetchedCount: unfetchedIds.length,
|
|
239
|
+
error: error instanceof Error ? error.message : String(error),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Build cache from referencedEventCache + whitelister tracking
|
|
245
|
+
const nudges: WhitelistItem[] = [];
|
|
246
|
+
const skills: WhitelistItem[] = [];
|
|
247
|
+
|
|
248
|
+
for (const [eventId, whitelisters] of eventToWhitelisters) {
|
|
249
|
+
const event = this.referencedEventCache.get(eventId);
|
|
250
|
+
if (!event) continue;
|
|
251
|
+
|
|
252
|
+
const whitelistedBy = Array.from(whitelisters);
|
|
253
|
+
|
|
254
|
+
if (event.kind === NDKKind.AgentNudge) {
|
|
255
|
+
nudges.push({
|
|
256
|
+
eventId: event.id,
|
|
257
|
+
kind: NDKKind.AgentNudge,
|
|
258
|
+
name: event.tagValue("title") || event.tagValue("name"),
|
|
259
|
+
description: event.content,
|
|
260
|
+
whitelistedBy,
|
|
261
|
+
});
|
|
262
|
+
} else if (event.kind === NDKKind.AgentSkill) {
|
|
263
|
+
skills.push({
|
|
264
|
+
eventId: event.id,
|
|
265
|
+
kind: NDKKind.AgentSkill,
|
|
266
|
+
name: event.tagValue("title") || event.tagValue("name"),
|
|
267
|
+
description: event.content,
|
|
268
|
+
whitelistedBy,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
this.cache = {
|
|
274
|
+
nudges,
|
|
275
|
+
skills,
|
|
276
|
+
lastUpdated: Date.now(),
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
span.setAttributes({
|
|
280
|
+
"whitelist.nudge_count": nudges.length,
|
|
281
|
+
"whitelist.skill_count": skills.length,
|
|
282
|
+
"whitelist.item_count": nudges.length + skills.length,
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
logger.info("[NudgeSkillWhitelistService] Cache rebuilt", {
|
|
286
|
+
nudgeCount: nudges.length,
|
|
287
|
+
skillCount: skills.length,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
291
|
+
span.end();
|
|
292
|
+
} catch (error) {
|
|
293
|
+
span.recordException(error as Error);
|
|
294
|
+
span.setStatus({
|
|
295
|
+
code: SpanStatusCode.ERROR,
|
|
296
|
+
message: (error as Error).message,
|
|
297
|
+
});
|
|
298
|
+
span.end();
|
|
299
|
+
logger.error("[NudgeSkillWhitelistService] Failed to rebuild cache", { error });
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Get all whitelisted nudges
|
|
306
|
+
*/
|
|
307
|
+
getWhitelistedNudges(): WhitelistItem[] {
|
|
308
|
+
if (!this.initialized) {
|
|
309
|
+
logger.warn("[NudgeSkillWhitelistService] getWhitelistedNudges called before initialize() — returning empty list");
|
|
310
|
+
}
|
|
311
|
+
return this.cache?.nudges || [];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Get all whitelisted skills
|
|
316
|
+
*/
|
|
317
|
+
getWhitelistedSkills(): WhitelistItem[] {
|
|
318
|
+
if (!this.initialized) {
|
|
319
|
+
logger.warn("[NudgeSkillWhitelistService] getWhitelistedSkills called before initialize() — returning empty list");
|
|
320
|
+
}
|
|
321
|
+
return this.cache?.skills || [];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Get all whitelisted items (both nudges and skills)
|
|
326
|
+
*/
|
|
327
|
+
getAllWhitelistedItems(): WhitelistItem[] {
|
|
328
|
+
if (!this.initialized) {
|
|
329
|
+
logger.warn("[NudgeSkillWhitelistService] getAllWhitelistedItems called before initialize() — returning empty list");
|
|
330
|
+
}
|
|
331
|
+
if (!this.cache) return [];
|
|
332
|
+
return [...this.cache.nudges, ...this.cache.skills];
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Check if a nudge event ID is whitelisted
|
|
337
|
+
*/
|
|
338
|
+
isNudgeWhitelisted(eventId: string): boolean {
|
|
339
|
+
return this.cache?.nudges.some(n => n.eventId === eventId) || false;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Check if a skill event ID is whitelisted
|
|
344
|
+
*/
|
|
345
|
+
isSkillWhitelisted(eventId: string): boolean {
|
|
346
|
+
return this.cache?.skills.some(s => s.eventId === eventId) || false;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Get a whitelisted nudge by event ID
|
|
351
|
+
*/
|
|
352
|
+
getNudge(eventId: string): WhitelistItem | undefined {
|
|
353
|
+
return this.cache?.nudges.find(n => n.eventId === eventId);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get the last cache update time
|
|
358
|
+
*/
|
|
359
|
+
getLastUpdated(): number | null {
|
|
360
|
+
return this.cache?.lastUpdated || null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Stop the subscription and clear all state.
|
|
365
|
+
* Used for cleanup during tests or shutdown.
|
|
366
|
+
*/
|
|
367
|
+
shutdown(): void {
|
|
368
|
+
if (this.subscription) {
|
|
369
|
+
this.subscription.stop();
|
|
370
|
+
this.subscription = null;
|
|
371
|
+
}
|
|
372
|
+
if (this.rebuildTimer) {
|
|
373
|
+
clearTimeout(this.rebuildTimer);
|
|
374
|
+
this.rebuildTimer = null;
|
|
375
|
+
}
|
|
376
|
+
this.cache = null;
|
|
377
|
+
this.initialized = false;
|
|
378
|
+
this.whitelistPubkeys.clear();
|
|
379
|
+
this.latestWhitelistEvents.clear();
|
|
380
|
+
this.referencedEventCache.clear();
|
|
381
|
+
}
|
|
382
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { NudgeService } from "./NudgeService";
|
|
2
|
+
export { NudgeSkillWhitelistService, NudgeSkillWhitelistService as NudgeWhitelistService } from "./NudgeWhitelistService";
|
|
3
|
+
export type { NudgeToolPermissions, NudgeResult, NudgeData } from "./types";
|
|
4
|
+
export type { WhitelistItem } from "./NudgeWhitelistService";
|
|
5
|
+
export { isOnlyToolMode, hasToolPermissions } from "./types";
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nudge Tool Permissions Types
|
|
3
|
+
*
|
|
4
|
+
* Defines the structure for tool permissions specified in nudge events (kind:4201).
|
|
5
|
+
* Nudges can modify an agent's available tools through three tag types:
|
|
6
|
+
*
|
|
7
|
+
* 1. only-tool: Highest priority - REPLACES all tools with only these
|
|
8
|
+
* 2. allow-tool: Adds tools to the agent's default set
|
|
9
|
+
* 3. deny-tool: Removes tools from the agent's default set
|
|
10
|
+
*
|
|
11
|
+
* Precedence: only-tool > allow-tool/deny-tool
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Tool permissions extracted from nudge event tags.
|
|
16
|
+
* Used to modify an agent's available tools during execution.
|
|
17
|
+
*/
|
|
18
|
+
export interface NudgeToolPermissions {
|
|
19
|
+
/**
|
|
20
|
+
* If set, the agent gets EXACTLY these tools (and nothing else).
|
|
21
|
+
* This is the highest priority - completely overrides allow/deny and agent defaults.
|
|
22
|
+
* Tag format: ["only-tool", "tool_name"]
|
|
23
|
+
*/
|
|
24
|
+
onlyTools?: string[];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Tools to enable (add to agent's default set).
|
|
28
|
+
* Ignored if onlyTools is set.
|
|
29
|
+
* Tag format: ["allow-tool", "tool_name"]
|
|
30
|
+
*/
|
|
31
|
+
allowTools?: string[];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Tools to disable (remove from agent's default set).
|
|
35
|
+
* Ignored if onlyTools is set.
|
|
36
|
+
* Tag format: ["deny-tool", "tool_name"]
|
|
37
|
+
*/
|
|
38
|
+
denyTools?: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Individual nudge data with content and title
|
|
43
|
+
*/
|
|
44
|
+
export interface NudgeData {
|
|
45
|
+
/** The nudge content/prompt */
|
|
46
|
+
content: string;
|
|
47
|
+
/** The nudge title (from "title" tag) */
|
|
48
|
+
title?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Result from fetching nudges with their tool permissions.
|
|
53
|
+
* Contains both the concatenated content for system prompt injection
|
|
54
|
+
* and the extracted tool permissions.
|
|
55
|
+
*/
|
|
56
|
+
export interface NudgeResult {
|
|
57
|
+
/** Individual nudge data for rendering in the fragment */
|
|
58
|
+
nudges: NudgeData[];
|
|
59
|
+
|
|
60
|
+
/** Concatenated content from all nudges (for backward compatibility) */
|
|
61
|
+
content: string;
|
|
62
|
+
|
|
63
|
+
/** Tool permissions extracted from all nudge tags */
|
|
64
|
+
toolPermissions: NudgeToolPermissions;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check if nudge permissions are using only-tool mode (highest priority)
|
|
69
|
+
*/
|
|
70
|
+
export function isOnlyToolMode(permissions: NudgeToolPermissions): boolean {
|
|
71
|
+
return Array.isArray(permissions.onlyTools) && permissions.onlyTools.length > 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if nudge permissions have any tool modifications
|
|
76
|
+
*/
|
|
77
|
+
export function hasToolPermissions(permissions: NudgeToolPermissions): boolean {
|
|
78
|
+
return (
|
|
79
|
+
isOnlyToolMode(permissions) ||
|
|
80
|
+
(Array.isArray(permissions.allowTools) && permissions.allowTools.length > 0) ||
|
|
81
|
+
(Array.isArray(permissions.denyTools) && permissions.denyTools.length > 0)
|
|
82
|
+
);
|
|
83
|
+
}
|