@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,611 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import type { AISdkTool, ToolExecutionContext } from "@/tools/types";
|
|
5
|
+
import { getProjectContext } from "@/services/projects";
|
|
6
|
+
import { config } from "@/services/ConfigService";
|
|
7
|
+
import { NDKAgentDefinition } from "@/events/NDKAgentDefinition";
|
|
8
|
+
import { getNDK } from "@/nostr/ndkClient";
|
|
9
|
+
import { Nip46SigningService } from "@/services/nip46";
|
|
10
|
+
import { agentStorage } from "@/agents/AgentStorage";
|
|
11
|
+
import { logger } from "@/utils/logger";
|
|
12
|
+
import { NDKEvent, NDKNip46Signer, NDKPrivateKeySigner, type NDKSigner } from "@nostr-dev-kit/ndk";
|
|
13
|
+
import { tool } from "ai";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
|
|
16
|
+
const MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
17
|
+
const UPLOAD_TIMEOUT_MS = 60_000;
|
|
18
|
+
const NIP46_CONNECT_TIMEOUT_MS = 30_000; // 30 seconds for NIP-46 connection
|
|
19
|
+
const NIP46_SIGNING_TIMEOUT_MS = 120_000; // 2 minutes for NIP-46 remote signing
|
|
20
|
+
|
|
21
|
+
const fileReferenceSchema = z.object({
|
|
22
|
+
path: z.string().describe("Absolute path to the file on disk"),
|
|
23
|
+
name: z.string().describe("Filename/relative path for the agent's home directory (used in the kind:1063 name tag)"),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const agentsPublishSchema = z.object({
|
|
27
|
+
slug: z.string().describe("The slug identifier of the agent to publish"),
|
|
28
|
+
description: z.string().describe("Short one-line description of the agent definition"),
|
|
29
|
+
category: z.string().describe("Category for the agent (e.g., 'developer', 'analyst', 'assistant')"),
|
|
30
|
+
rich_description: z.string().describe("Comprehensive homepage-style description of what the agent definition is all about (markdown). This becomes the event content."),
|
|
31
|
+
image: z
|
|
32
|
+
.string()
|
|
33
|
+
.url("image must be a valid URL")
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Optional image URL for the agent definition. Published as an 'image' tag in the kind:4199 event."),
|
|
36
|
+
publish_as_user: z
|
|
37
|
+
.boolean()
|
|
38
|
+
.default(true)
|
|
39
|
+
.describe(
|
|
40
|
+
"When true (default), the kind:4199 event is signed by the project owner via NIP-46 remote signing " +
|
|
41
|
+
"(the agent sends the signing request). When false, the event is signed with the TENEX backend key."
|
|
42
|
+
),
|
|
43
|
+
files: z
|
|
44
|
+
.array(fileReferenceSchema)
|
|
45
|
+
.optional()
|
|
46
|
+
.describe(
|
|
47
|
+
"Optional array of files to bundle with the agent. Each file will be uploaded to Blossom, " +
|
|
48
|
+
"a kind:1063 NIP-94 event created, and an e-tag added to the agent definition."
|
|
49
|
+
),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
type AgentsPublishInput = z.infer<typeof agentsPublishSchema>;
|
|
53
|
+
|
|
54
|
+
interface BlossomConfig {
|
|
55
|
+
serverUrl: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface UploadResult {
|
|
59
|
+
url: string;
|
|
60
|
+
sha256: string;
|
|
61
|
+
size: number;
|
|
62
|
+
type?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface FileMetadataEvent {
|
|
66
|
+
eventId: string;
|
|
67
|
+
name: string;
|
|
68
|
+
url: string;
|
|
69
|
+
sha256: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get Blossom server configuration from global config
|
|
74
|
+
*/
|
|
75
|
+
async function getBlossomConfig(): Promise<BlossomConfig> {
|
|
76
|
+
try {
|
|
77
|
+
const tenexConfig = await config.loadTenexConfig(config.getGlobalPath());
|
|
78
|
+
return {
|
|
79
|
+
serverUrl: tenexConfig.blossomServerUrl || "https://blossom.primal.net",
|
|
80
|
+
};
|
|
81
|
+
} catch {
|
|
82
|
+
return {
|
|
83
|
+
serverUrl: "https://blossom.primal.net",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Detect MIME type from file extension
|
|
90
|
+
*/
|
|
91
|
+
function detectMimeType(filePath: string): string {
|
|
92
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
93
|
+
const mimeTypes: Record<string, string> = {
|
|
94
|
+
".jpg": "image/jpeg",
|
|
95
|
+
".jpeg": "image/jpeg",
|
|
96
|
+
".png": "image/png",
|
|
97
|
+
".gif": "image/gif",
|
|
98
|
+
".webp": "image/webp",
|
|
99
|
+
".mp4": "video/mp4",
|
|
100
|
+
".mov": "video/quicktime",
|
|
101
|
+
".avi": "video/x-msvideo",
|
|
102
|
+
".webm": "video/webm",
|
|
103
|
+
".mp3": "audio/mpeg",
|
|
104
|
+
".wav": "audio/wav",
|
|
105
|
+
".pdf": "application/pdf",
|
|
106
|
+
".json": "application/json",
|
|
107
|
+
".txt": "text/plain",
|
|
108
|
+
".md": "text/markdown",
|
|
109
|
+
".js": "text/javascript",
|
|
110
|
+
".ts": "text/typescript",
|
|
111
|
+
".py": "text/x-python",
|
|
112
|
+
".sh": "text/x-shellscript",
|
|
113
|
+
".rb": "text/x-ruby",
|
|
114
|
+
".pl": "text/x-perl",
|
|
115
|
+
};
|
|
116
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Calculate SHA256 hash of data
|
|
121
|
+
*/
|
|
122
|
+
function calculateSHA256(data: Buffer): string {
|
|
123
|
+
return crypto.createHash("sha256").update(data).digest("hex");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create Blossom authorization event (kind 24242)
|
|
128
|
+
*/
|
|
129
|
+
async function createBlossomAuthEvent(
|
|
130
|
+
sha256Hash: string,
|
|
131
|
+
description: string,
|
|
132
|
+
signer: NDKSigner
|
|
133
|
+
): Promise<NDKEvent> {
|
|
134
|
+
const event = new NDKEvent();
|
|
135
|
+
event.kind = 24242;
|
|
136
|
+
event.content = description;
|
|
137
|
+
event.created_at = Math.floor(Date.now() / 1000);
|
|
138
|
+
event.tags = [
|
|
139
|
+
["t", "upload"],
|
|
140
|
+
["x", sha256Hash],
|
|
141
|
+
["expiration", String(Math.floor(Date.now() / 1000) + 3600)], // 1 hour expiration
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
await event.sign(signer);
|
|
145
|
+
return event;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Upload data to Blossom server
|
|
150
|
+
*/
|
|
151
|
+
async function uploadToBlossomServer(
|
|
152
|
+
serverUrl: string,
|
|
153
|
+
data: Buffer,
|
|
154
|
+
mimeType: string,
|
|
155
|
+
authEvent: NDKEvent
|
|
156
|
+
): Promise<UploadResult> {
|
|
157
|
+
const authHeader = `Nostr ${Buffer.from(JSON.stringify(authEvent.rawEvent())).toString("base64")}`;
|
|
158
|
+
|
|
159
|
+
const controller = new AbortController();
|
|
160
|
+
const timeout = setTimeout(() => controller.abort(), UPLOAD_TIMEOUT_MS);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const response = await fetch(`${serverUrl}/upload`, {
|
|
164
|
+
method: "PUT",
|
|
165
|
+
headers: {
|
|
166
|
+
Authorization: authHeader,
|
|
167
|
+
"Content-Type": mimeType,
|
|
168
|
+
"Content-Length": String(data.length),
|
|
169
|
+
},
|
|
170
|
+
body: data,
|
|
171
|
+
signal: controller.signal,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
if (!response.ok) {
|
|
175
|
+
let errorMessage = `Upload failed with status ${response.status}`;
|
|
176
|
+
try {
|
|
177
|
+
const errorData = (await response.json()) as { message?: string };
|
|
178
|
+
if (errorData.message) {
|
|
179
|
+
errorMessage = `Upload failed: ${errorData.message}`;
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
// If parsing JSON fails, use the default error message
|
|
183
|
+
}
|
|
184
|
+
throw new Error(errorMessage);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const result = (await response.json()) as UploadResult;
|
|
188
|
+
return result;
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
191
|
+
throw new Error(`Upload timed out after ${UPLOAD_TIMEOUT_MS}ms`, { cause: error });
|
|
192
|
+
}
|
|
193
|
+
throw error;
|
|
194
|
+
} finally {
|
|
195
|
+
clearTimeout(timeout);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Create a kind:1063 NIP-94 file metadata event
|
|
201
|
+
*/
|
|
202
|
+
async function createFileMetadataEvent(
|
|
203
|
+
url: string,
|
|
204
|
+
sha256: string,
|
|
205
|
+
name: string,
|
|
206
|
+
mimeType: string,
|
|
207
|
+
size: number,
|
|
208
|
+
signer: NDKSigner,
|
|
209
|
+
ndk: ReturnType<typeof getNDK>
|
|
210
|
+
): Promise<FileMetadataEvent> {
|
|
211
|
+
const event = new NDKEvent(ndk);
|
|
212
|
+
event.kind = 1063;
|
|
213
|
+
event.content = "";
|
|
214
|
+
event.created_at = Math.floor(Date.now() / 1000);
|
|
215
|
+
event.tags = [
|
|
216
|
+
["url", url],
|
|
217
|
+
["name", name],
|
|
218
|
+
["m", mimeType],
|
|
219
|
+
["x", sha256],
|
|
220
|
+
["size", String(size)],
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
await event.sign(signer);
|
|
224
|
+
await event.publish();
|
|
225
|
+
|
|
226
|
+
logger.info(`Published kind:1063 file metadata event`, {
|
|
227
|
+
eventId: event.id,
|
|
228
|
+
name,
|
|
229
|
+
url,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
eventId: event.id,
|
|
234
|
+
name,
|
|
235
|
+
url,
|
|
236
|
+
sha256,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Upload a file to Blossom and create the kind:1063 metadata event
|
|
242
|
+
*/
|
|
243
|
+
async function uploadFileAndCreateMetadata(
|
|
244
|
+
filePath: string,
|
|
245
|
+
name: string,
|
|
246
|
+
signer: NDKSigner,
|
|
247
|
+
ndk: ReturnType<typeof getNDK>
|
|
248
|
+
): Promise<FileMetadataEvent> {
|
|
249
|
+
// Validate file exists
|
|
250
|
+
try {
|
|
251
|
+
await fs.access(filePath);
|
|
252
|
+
} catch {
|
|
253
|
+
throw new Error(`File not found: ${filePath}`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Check file size
|
|
257
|
+
const stats = await fs.stat(filePath);
|
|
258
|
+
if (stats.size > MAX_FILE_SIZE_BYTES) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`File size ${stats.size} bytes exceeds limit of ${MAX_FILE_SIZE_BYTES} bytes`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Read file
|
|
265
|
+
const data = await fs.readFile(filePath);
|
|
266
|
+
const mimeType = detectMimeType(filePath);
|
|
267
|
+
const sha256 = calculateSHA256(data);
|
|
268
|
+
|
|
269
|
+
logger.info(`Uploading file to Blossom`, {
|
|
270
|
+
path: filePath,
|
|
271
|
+
name,
|
|
272
|
+
size: data.length,
|
|
273
|
+
mimeType,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Get Blossom config and upload
|
|
277
|
+
const blossomConfig = await getBlossomConfig();
|
|
278
|
+
const authEvent = await createBlossomAuthEvent(sha256, `Upload ${name}`, signer);
|
|
279
|
+
const uploadResult = await uploadToBlossomServer(
|
|
280
|
+
blossomConfig.serverUrl,
|
|
281
|
+
data,
|
|
282
|
+
mimeType,
|
|
283
|
+
authEvent
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
logger.info(`File uploaded to Blossom`, {
|
|
287
|
+
name,
|
|
288
|
+
url: uploadResult.url,
|
|
289
|
+
sha256: uploadResult.sha256,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Create kind:1063 event
|
|
293
|
+
const metadataEvent = await createFileMetadataEvent(
|
|
294
|
+
uploadResult.url,
|
|
295
|
+
uploadResult.sha256,
|
|
296
|
+
name,
|
|
297
|
+
mimeType,
|
|
298
|
+
data.length,
|
|
299
|
+
signer,
|
|
300
|
+
ndk
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
return metadataEvent;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Create an NDKNip46Signer using the agent's private key as the local signer
|
|
308
|
+
* and the owner's pubkey as the remote signer target.
|
|
309
|
+
* Returns the signer after connecting (with timeout).
|
|
310
|
+
*/
|
|
311
|
+
async function createAgentNip46Signer(
|
|
312
|
+
agentSigner: NDKPrivateKeySigner,
|
|
313
|
+
ownerPubkey: string,
|
|
314
|
+
): Promise<NDKNip46Signer> {
|
|
315
|
+
const ndk = getNDK();
|
|
316
|
+
const nip46Service = Nip46SigningService.getInstance();
|
|
317
|
+
const bunkerUri = nip46Service.getBunkerUri(ownerPubkey);
|
|
318
|
+
|
|
319
|
+
if (!bunkerUri || !bunkerUri.startsWith("bunker://")) {
|
|
320
|
+
throw new Error(
|
|
321
|
+
`Invalid bunker URI for owner ${ownerPubkey.substring(0, 12)}: ` +
|
|
322
|
+
`expected a "bunker://" URI but got "${bunkerUri || "(empty)"}"`
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
logger.info("[agents_publish] Creating NIP-46 signer for remote signing", {
|
|
327
|
+
ownerPubkey: ownerPubkey.substring(0, 12),
|
|
328
|
+
bunkerUri: bunkerUri.substring(0, 60),
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
const signer = NDKNip46Signer.bunker(ndk, bunkerUri, agentSigner);
|
|
332
|
+
|
|
333
|
+
signer.on("authUrl", (url: string) => {
|
|
334
|
+
logger.info("[agents_publish] NIP-46 auth URL required", {
|
|
335
|
+
ownerPubkey: ownerPubkey.substring(0, 12),
|
|
336
|
+
url,
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// Connect with timeout — cancel timer on success to avoid leaked handles
|
|
341
|
+
let connectTimer: ReturnType<typeof setTimeout> | undefined;
|
|
342
|
+
try {
|
|
343
|
+
await Promise.race([
|
|
344
|
+
signer.blockUntilReady(),
|
|
345
|
+
new Promise<never>((_, reject) => {
|
|
346
|
+
connectTimer = setTimeout(
|
|
347
|
+
() => reject(new Error(
|
|
348
|
+
`NIP-46 connect timed out after ${NIP46_CONNECT_TIMEOUT_MS / 1000}s`
|
|
349
|
+
)),
|
|
350
|
+
NIP46_CONNECT_TIMEOUT_MS,
|
|
351
|
+
);
|
|
352
|
+
}),
|
|
353
|
+
]);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
// On timeout or connection failure, close the signer to release relay subscriptions
|
|
356
|
+
try { signer.stop(); } catch { /* best-effort cleanup */ }
|
|
357
|
+
throw error;
|
|
358
|
+
} finally {
|
|
359
|
+
clearTimeout(connectTimer);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return signer;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Publishes an agent definition (kind 4199) to Nostr.
|
|
367
|
+
*
|
|
368
|
+
* When `publish_as_user` is true, uses NIP-46 remote signing so the event
|
|
369
|
+
* is signed by the project owner (the agent acts as the NIP-46 client).
|
|
370
|
+
* When false, signs with the TENEX backend key.
|
|
371
|
+
*
|
|
372
|
+
* Optionally uploads files and references them via e-tags.
|
|
373
|
+
* Returns the event ID on success.
|
|
374
|
+
*/
|
|
375
|
+
async function executeAgentsPublish(
|
|
376
|
+
input: AgentsPublishInput,
|
|
377
|
+
context: ToolExecutionContext,
|
|
378
|
+
): Promise<string> {
|
|
379
|
+
const { slug, description, category, rich_description, image, publish_as_user, files } = input;
|
|
380
|
+
|
|
381
|
+
if (!slug) {
|
|
382
|
+
throw new Error("Agent slug is required");
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const projectCtx = getProjectContext();
|
|
386
|
+
const agent = projectCtx.getAgent(slug);
|
|
387
|
+
|
|
388
|
+
if (!agent) {
|
|
389
|
+
throw new Error(`Agent with slug "${slug}" not found in current project`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const ndk = getNDK();
|
|
393
|
+
|
|
394
|
+
// Backend signer is only needed when NOT publishing as user (backend signs the 4199 event).
|
|
395
|
+
// File uploads are always signed by the agent's own signer when publish_as_user=true.
|
|
396
|
+
const backendSigner = !publish_as_user ? await config.getBackendSigner() : undefined;
|
|
397
|
+
|
|
398
|
+
// Choose the signer for file uploads: agent's own signer when publishing as user, backend signer otherwise
|
|
399
|
+
const fileSigner: NDKSigner = publish_as_user ? context.agent.signer : backendSigner!;
|
|
400
|
+
|
|
401
|
+
// Upload files and create kind:1063 events if provided
|
|
402
|
+
const fileMetadataEvents: FileMetadataEvent[] = [];
|
|
403
|
+
if (files && files.length > 0) {
|
|
404
|
+
logger.info(`Uploading ${files.length} file(s) for agent "${agent.name}"`);
|
|
405
|
+
|
|
406
|
+
for (const file of files) {
|
|
407
|
+
try {
|
|
408
|
+
const metadata = await uploadFileAndCreateMetadata(
|
|
409
|
+
file.path,
|
|
410
|
+
file.name,
|
|
411
|
+
fileSigner,
|
|
412
|
+
ndk
|
|
413
|
+
);
|
|
414
|
+
fileMetadataEvents.push(metadata);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
417
|
+
logger.error(`Failed to upload file: ${file.path}`, { error: errorMessage });
|
|
418
|
+
throw new Error(`Failed to upload file "${file.name}": ${errorMessage}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Create the agent definition event
|
|
424
|
+
const agentDefinition = new NDKAgentDefinition(ndk);
|
|
425
|
+
|
|
426
|
+
agentDefinition.title = agent.name;
|
|
427
|
+
agentDefinition.role = agent.role;
|
|
428
|
+
agentDefinition.description = description;
|
|
429
|
+
agentDefinition.category = category;
|
|
430
|
+
// Rich description goes into event content, not a tag
|
|
431
|
+
agentDefinition.content = rich_description;
|
|
432
|
+
|
|
433
|
+
if (image) {
|
|
434
|
+
agentDefinition.removeTag("image");
|
|
435
|
+
agentDefinition.tags.push(["image", image]);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (agent.instructions) {
|
|
439
|
+
agentDefinition.instructions = agent.instructions;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (agent.useCriteria) {
|
|
443
|
+
agentDefinition.useCriteria = agent.useCriteria;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
agentDefinition.version = 1;
|
|
447
|
+
|
|
448
|
+
agentDefinition.slug = slug;
|
|
449
|
+
|
|
450
|
+
// Add e-tags for each file metadata event
|
|
451
|
+
for (const fileMetadata of fileMetadataEvents) {
|
|
452
|
+
agentDefinition.tags.push(["e", fileMetadata.eventId]);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Determine signing strategy
|
|
456
|
+
if (publish_as_user) {
|
|
457
|
+
// NIP-46 remote signing: the agent sends the signing request to the owner
|
|
458
|
+
const nip46Service = Nip46SigningService.getInstance();
|
|
459
|
+
|
|
460
|
+
if (!nip46Service.isEnabled()) {
|
|
461
|
+
throw new Error(
|
|
462
|
+
"Cannot publish as user: NIP-46 remote signing is not enabled in the configuration. " +
|
|
463
|
+
"Enable it by setting nip46.enabled=true in your TENEX config, or set publish_as_user=false " +
|
|
464
|
+
"to publish with the backend signer instead."
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const ownerPubkey = projectCtx?.project?.pubkey;
|
|
469
|
+
if (!ownerPubkey) {
|
|
470
|
+
throw new Error(
|
|
471
|
+
"Cannot publish as user: no project owner pubkey found. " +
|
|
472
|
+
"Set publish_as_user=false to publish with the backend signer instead."
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Runtime type guard: verify agent signer is NDKPrivateKeySigner (required for NIP-46 local key)
|
|
477
|
+
const agentSigner: unknown = context.agent.signer;
|
|
478
|
+
if (!(agentSigner instanceof NDKPrivateKeySigner)) {
|
|
479
|
+
throw new Error(
|
|
480
|
+
`Expected agent signer to be NDKPrivateKeySigner for NIP-46 signing, ` +
|
|
481
|
+
`but got ${(agentSigner as { constructor?: { name?: string } })?.constructor?.name ?? "undefined"}. ` +
|
|
482
|
+
`Agent "${slug}" may have an incompatible signer configuration.`
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
logger.info(`Publishing agent definition as user via NIP-46`, {
|
|
487
|
+
slug,
|
|
488
|
+
ownerPubkey: ownerPubkey.substring(0, 12),
|
|
489
|
+
agentPubkey: context.agent.pubkey.substring(0, 12),
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// Create NIP-46 signer using the agent's own signer as local key
|
|
493
|
+
const nip46Signer = await createAgentNip46Signer(
|
|
494
|
+
context.agent.signer,
|
|
495
|
+
ownerPubkey,
|
|
496
|
+
);
|
|
497
|
+
|
|
498
|
+
// Set pubkey to the owner's before signing
|
|
499
|
+
agentDefinition.pubkey = ownerPubkey;
|
|
500
|
+
|
|
501
|
+
// Sign with NIP-46 (with timeout) — cancel timer and close signer on failure
|
|
502
|
+
let signingTimer: ReturnType<typeof setTimeout> | undefined;
|
|
503
|
+
try {
|
|
504
|
+
await Promise.race([
|
|
505
|
+
agentDefinition.sign(nip46Signer, { pTags: false }),
|
|
506
|
+
new Promise<never>((_, reject) => {
|
|
507
|
+
signingTimer = setTimeout(
|
|
508
|
+
() => reject(new Error(
|
|
509
|
+
`NIP-46 remote signing timed out after ${NIP46_SIGNING_TIMEOUT_MS / 1000}s`
|
|
510
|
+
)),
|
|
511
|
+
NIP46_SIGNING_TIMEOUT_MS,
|
|
512
|
+
);
|
|
513
|
+
}),
|
|
514
|
+
]);
|
|
515
|
+
} catch (error) {
|
|
516
|
+
try { nip46Signer.stop(); } catch { /* best-effort cleanup */ }
|
|
517
|
+
throw error;
|
|
518
|
+
} finally {
|
|
519
|
+
clearTimeout(signingTimer);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
try {
|
|
523
|
+
await agentDefinition.publish();
|
|
524
|
+
} finally {
|
|
525
|
+
// Clean up the NIP-46 signer whether publish succeeds or fails
|
|
526
|
+
try { nip46Signer.stop(); } catch { /* best-effort cleanup */ }
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
logger.info(`Successfully published user-signed agent definition for "${agent.name}" (${slug})`, {
|
|
530
|
+
eventId: agentDefinition.id,
|
|
531
|
+
pubkey: ownerPubkey,
|
|
532
|
+
signerType: "nip46",
|
|
533
|
+
filesAttached: fileMetadataEvents.length,
|
|
534
|
+
});
|
|
535
|
+
} else {
|
|
536
|
+
// Backend signer (original behavior)
|
|
537
|
+
agentDefinition.pubkey = backendSigner!.pubkey;
|
|
538
|
+
|
|
539
|
+
await agentDefinition.sign(backendSigner!, { pTags: false });
|
|
540
|
+
await agentDefinition.publish();
|
|
541
|
+
|
|
542
|
+
logger.info(`Successfully published backend-signed agent definition for "${agent.name}" (${slug})`, {
|
|
543
|
+
eventId: agentDefinition.id,
|
|
544
|
+
pubkey: backendSigner!.pubkey,
|
|
545
|
+
signerType: "backend",
|
|
546
|
+
filesAttached: fileMetadataEvents.length,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Sync published metadata back to local storage so the AgentDefinitionMonitor
|
|
551
|
+
// can track this agent for future updates (needs definitionDTag + definitionAuthor).
|
|
552
|
+
// Use the published agent's pubkey (resolved by slug), NOT context.agent.pubkey
|
|
553
|
+
// which is the *calling* agent — they may differ when one agent publishes another.
|
|
554
|
+
try {
|
|
555
|
+
const storedAgent = await agentStorage.loadAgent(agent.pubkey);
|
|
556
|
+
if (storedAgent) {
|
|
557
|
+
storedAgent.eventId = agentDefinition.id;
|
|
558
|
+
storedAgent.definitionDTag = agentDefinition.tagValue("d") ?? slug;
|
|
559
|
+
storedAgent.definitionAuthor = agentDefinition.pubkey;
|
|
560
|
+
storedAgent.definitionCreatedAt = agentDefinition.created_at;
|
|
561
|
+
await agentStorage.saveAgent(storedAgent);
|
|
562
|
+
|
|
563
|
+
logger.info("[agents_publish] Synced publish metadata to local storage", {
|
|
564
|
+
slug,
|
|
565
|
+
eventId: agentDefinition.id?.substring(0, 12),
|
|
566
|
+
definitionDTag: storedAgent.definitionDTag,
|
|
567
|
+
definitionAuthor: storedAgent.definitionAuthor?.substring(0, 12),
|
|
568
|
+
});
|
|
569
|
+
} else {
|
|
570
|
+
logger.warn("[agents_publish] Could not find stored agent to sync publish metadata", {
|
|
571
|
+
slug,
|
|
572
|
+
pubkey: agent.pubkey?.substring(0, 12),
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
} catch (error) {
|
|
576
|
+
// Never block publish success due to a storage sync failure
|
|
577
|
+
logger.error("[agents_publish] Failed to sync publish metadata to local storage", {
|
|
578
|
+
slug,
|
|
579
|
+
error: error instanceof Error ? error.message : String(error),
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return agentDefinition.id;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Create an AI SDK tool for publishing agent definitions
|
|
588
|
+
*/
|
|
589
|
+
export function createAgentsPublishTool(context: ToolExecutionContext): AISdkTool {
|
|
590
|
+
return tool({
|
|
591
|
+
description:
|
|
592
|
+
"Publish an agent definition (kind 4199) to Nostr. " +
|
|
593
|
+
"By default, the event is signed by the project owner via NIP-46 remote signing " +
|
|
594
|
+
"(the agent sends the signing request). Set publish_as_user=false to sign with the TENEX backend key instead. " +
|
|
595
|
+
"Optionally include an image URL and/or an array of files to bundle with the agent. " +
|
|
596
|
+
"Each file is uploaded to Blossom, a kind:1063 NIP-94 event is created, and an e-tag " +
|
|
597
|
+
"is added to the agent definition referencing the file. Returns the event ID on success.",
|
|
598
|
+
inputSchema: agentsPublishSchema,
|
|
599
|
+
execute: async (input: AgentsPublishInput) => {
|
|
600
|
+
try {
|
|
601
|
+
return await executeAgentsPublish(input, context);
|
|
602
|
+
} catch (error) {
|
|
603
|
+
logger.error("Failed to publish agent definition", { error });
|
|
604
|
+
throw new Error(
|
|
605
|
+
`Failed to publish agent definition: ${error instanceof Error ? error.message : String(error)}`,
|
|
606
|
+
{ cause: error }
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
}) as AISdkTool;
|
|
611
|
+
}
|