@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,657 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import { agentStorage } from "@/agents/AgentStorage";
|
|
3
|
+
import { NDKKind } from "@/nostr/kinds";
|
|
4
|
+
import { getNDK } from "@/nostr/ndkClient";
|
|
5
|
+
import { config } from "@/services/ConfigService";
|
|
6
|
+
import { Nip46SigningService, Nip46SigningLog } from "@/services/nip46";
|
|
7
|
+
import { logger } from "@/utils/logger";
|
|
8
|
+
import {
|
|
9
|
+
NDKEvent,
|
|
10
|
+
NDKPrivateKeySigner,
|
|
11
|
+
type NDKProject,
|
|
12
|
+
} from "@nostr-dev-kit/ndk";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Publishes Nostr events for agent profiles and creation.
|
|
16
|
+
* Separated from AgentPublisher to handle agent setup vs runtime publishing.
|
|
17
|
+
*/
|
|
18
|
+
export class AgentProfilePublisher {
|
|
19
|
+
/** Timeout in milliseconds for publish operations */
|
|
20
|
+
private static readonly PUBLISH_TIMEOUT_MS = 5000;
|
|
21
|
+
|
|
22
|
+
/** Avatar style families for deterministic avatar selection */
|
|
23
|
+
private static readonly AVATAR_FAMILIES = [
|
|
24
|
+
"lorelei",
|
|
25
|
+
"miniavs",
|
|
26
|
+
"dylan",
|
|
27
|
+
"pixel-art",
|
|
28
|
+
"rings",
|
|
29
|
+
"avataaars",
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Builds a deterministic avatar URL based on the pubkey.
|
|
34
|
+
* Uses DiceBear API with a family selected based on pubkey hash.
|
|
35
|
+
*/
|
|
36
|
+
private static buildAvatarUrl(pubkey: string): string {
|
|
37
|
+
const familyIndex =
|
|
38
|
+
Number.parseInt(pubkey.substring(0, 8), 16) % AgentProfilePublisher.AVATAR_FAMILIES.length;
|
|
39
|
+
const avatarStyle = AgentProfilePublisher.AVATAR_FAMILIES[familyIndex];
|
|
40
|
+
return `https://api.dicebear.com/7.x/${avatarStyle}/png?seed=${pubkey}`;
|
|
41
|
+
}
|
|
42
|
+
/** Per-project debounce timers for 14199 snapshot publishing */
|
|
43
|
+
private static snapshotDebounceTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();
|
|
44
|
+
private static readonly SNAPSHOT_DEBOUNCE_MS = 5000;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Schedule a kind:14199 snapshot publish for a specific project.
|
|
48
|
+
* Debounced per-project: each project's agents are additively merged into
|
|
49
|
+
* the owner's single 14199 event without removing other projects' agents.
|
|
50
|
+
*/
|
|
51
|
+
static publishProjectAgentSnapshot(projectDTag: string): void {
|
|
52
|
+
const existing = AgentProfilePublisher.snapshotDebounceTimers.get(projectDTag);
|
|
53
|
+
if (existing) {
|
|
54
|
+
clearTimeout(existing);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const timer = setTimeout(() => {
|
|
58
|
+
AgentProfilePublisher.snapshotDebounceTimers.delete(projectDTag);
|
|
59
|
+
AgentProfilePublisher.executeSnapshotPublish(projectDTag).catch((error) => {
|
|
60
|
+
logger.warn("Debounced 14199 snapshot publish failed", {
|
|
61
|
+
projectDTag,
|
|
62
|
+
error: error instanceof Error ? error.message : String(error),
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}, AgentProfilePublisher.SNAPSHOT_DEBOUNCE_MS);
|
|
66
|
+
|
|
67
|
+
AgentProfilePublisher.snapshotDebounceTimers.set(projectDTag, timer);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Execute the actual 14199 snapshot publish for a specific project.
|
|
72
|
+
* Reads agents for this project only, then additively merges their pubkeys
|
|
73
|
+
* into the owner's existing 14199 event (preserving agents from other projects).
|
|
74
|
+
*
|
|
75
|
+
* When NIP-46 is enabled, each whitelisted owner gets their own 14199 event signed
|
|
76
|
+
* by that owner via NIP-46 remote signing. If signing fails, the event is simply
|
|
77
|
+
* not published — there is no fallback to backend key signing.
|
|
78
|
+
*
|
|
79
|
+
* When NIP-46 is disabled, the event is signed with the backend key.
|
|
80
|
+
*/
|
|
81
|
+
private static async executeSnapshotPublish(projectDTag: string): Promise<void> {
|
|
82
|
+
const projectAgents = await agentStorage.getProjectAgents(projectDTag);
|
|
83
|
+
const whitelisted = config.getWhitelistedPubkeys(undefined, config.getConfig());
|
|
84
|
+
|
|
85
|
+
// Collect unique agent pubkeys for this project
|
|
86
|
+
const agentPubkeys: string[] = [];
|
|
87
|
+
const seen = new Set<string>();
|
|
88
|
+
for (const agent of projectAgents) {
|
|
89
|
+
const agentSigner = new NDKPrivateKeySigner(agent.nsec);
|
|
90
|
+
if (!seen.has(agentSigner.pubkey)) {
|
|
91
|
+
seen.add(agentSigner.pubkey);
|
|
92
|
+
agentPubkeys.push(agentSigner.pubkey);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
logger.info("Publishing debounced 14199 snapshot", {
|
|
97
|
+
projectDTag,
|
|
98
|
+
agentCount: agentPubkeys.length,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const nip46Service = Nip46SigningService.getInstance();
|
|
102
|
+
|
|
103
|
+
if (nip46Service.isEnabled()) {
|
|
104
|
+
for (const ownerPubkey of whitelisted) {
|
|
105
|
+
await AgentProfilePublisher.publishSnapshotForOwner(
|
|
106
|
+
ownerPubkey,
|
|
107
|
+
agentPubkeys,
|
|
108
|
+
nip46Service,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
await AgentProfilePublisher.publishSnapshotWithBackendKey(agentPubkeys);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Publish a 14199 snapshot signed by a specific owner via NIP-46.
|
|
118
|
+
* Additively merges this project's agent pubkeys into the owner's existing 14199.
|
|
119
|
+
* If all project agents are already present, skips the publish entirely.
|
|
120
|
+
* If signing fails for any reason, the event is not published.
|
|
121
|
+
*/
|
|
122
|
+
private static async publishSnapshotForOwner(
|
|
123
|
+
ownerPubkey: string,
|
|
124
|
+
projectAgentPubkeys: string[],
|
|
125
|
+
nip46Service: Nip46SigningService,
|
|
126
|
+
): Promise<void> {
|
|
127
|
+
const existingPTags = await AgentProfilePublisher.fetchExistingPTags(ownerPubkey);
|
|
128
|
+
const existingSet = new Set(existingPTags);
|
|
129
|
+
const newPubkeys = projectAgentPubkeys.filter((pk) => !existingSet.has(pk));
|
|
130
|
+
|
|
131
|
+
if (newPubkeys.length === 0 && existingPTags.length > 0) {
|
|
132
|
+
logger.debug("[NIP-46] All project agents already in 14199, skipping publish", {
|
|
133
|
+
ownerPubkey: ownerPubkey.substring(0, 12),
|
|
134
|
+
existingCount: existingPTags.length,
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const mergedPubkeys = [...existingPTags, ...newPubkeys];
|
|
140
|
+
const ndk = getNDK();
|
|
141
|
+
const signingLog = Nip46SigningLog.getInstance();
|
|
142
|
+
const ev = AgentProfilePublisher.buildSnapshotEvent(ndk, mergedPubkeys);
|
|
143
|
+
|
|
144
|
+
const result = await nip46Service.signEvent(ownerPubkey, ev, "14199_snapshot");
|
|
145
|
+
|
|
146
|
+
if (result.outcome === "signed") {
|
|
147
|
+
try {
|
|
148
|
+
await ev.publish();
|
|
149
|
+
signingLog.log({
|
|
150
|
+
op: "event_published",
|
|
151
|
+
ownerPubkey: Nip46SigningLog.truncatePubkey(ownerPubkey),
|
|
152
|
+
eventKind: NDKKind.ProjectAgentSnapshot as number,
|
|
153
|
+
signerType: "nip46",
|
|
154
|
+
pTagCount: ev.tags.filter((t) => t[0] === "p").length,
|
|
155
|
+
eventId: ev.id,
|
|
156
|
+
});
|
|
157
|
+
logger.info("[NIP-46] Published owner-signed 14199", {
|
|
158
|
+
ownerPubkey: ownerPubkey.substring(0, 12),
|
|
159
|
+
eventId: ev.id?.substring(0, 12),
|
|
160
|
+
existingPTags: existingPTags.length,
|
|
161
|
+
newPubkeys: newPubkeys.length,
|
|
162
|
+
totalPTags: mergedPubkeys.length,
|
|
163
|
+
});
|
|
164
|
+
} catch (error) {
|
|
165
|
+
logger.warn("[NIP-46] Failed to publish owner-signed 14199", {
|
|
166
|
+
ownerPubkey: ownerPubkey.substring(0, 12),
|
|
167
|
+
error: error instanceof Error ? error.message : String(error),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
logger.warn("[NIP-46] Skipping 14199 publish — signing failed", {
|
|
174
|
+
ownerPubkey: ownerPubkey.substring(0, 12),
|
|
175
|
+
outcome: result.outcome,
|
|
176
|
+
reason: result.reason,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Publish a 14199 snapshot signed with the backend key.
|
|
182
|
+
* Additively merges this project's agent pubkeys into the existing 14199.
|
|
183
|
+
* If all project agents are already present, skips the publish entirely.
|
|
184
|
+
* Used when NIP-46 is not enabled.
|
|
185
|
+
*/
|
|
186
|
+
private static async publishSnapshotWithBackendKey(
|
|
187
|
+
projectAgentPubkeys: string[],
|
|
188
|
+
): Promise<void> {
|
|
189
|
+
const tenexNsec = await config.ensureBackendPrivateKey();
|
|
190
|
+
const signer = new NDKPrivateKeySigner(tenexNsec);
|
|
191
|
+
|
|
192
|
+
const existingPTags = await AgentProfilePublisher.fetchExistingPTags(signer.pubkey);
|
|
193
|
+
const existingSet = new Set(existingPTags);
|
|
194
|
+
const newPubkeys = projectAgentPubkeys.filter((pk) => !existingSet.has(pk));
|
|
195
|
+
|
|
196
|
+
if (newPubkeys.length === 0 && existingPTags.length > 0) {
|
|
197
|
+
logger.debug("All project agents already in 14199, skipping backend publish", {
|
|
198
|
+
existingCount: existingPTags.length,
|
|
199
|
+
});
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const mergedPubkeys = [...existingPTags, ...newPubkeys];
|
|
204
|
+
const ndk = getNDK();
|
|
205
|
+
const ev = AgentProfilePublisher.buildSnapshotEvent(ndk, mergedPubkeys);
|
|
206
|
+
|
|
207
|
+
await ev.sign(signer);
|
|
208
|
+
try {
|
|
209
|
+
await ev.publish();
|
|
210
|
+
logger.info("Published backend-signed 14199 snapshot", {
|
|
211
|
+
existingPTags: existingPTags.length,
|
|
212
|
+
newPubkeys: newPubkeys.length,
|
|
213
|
+
totalPTags: mergedPubkeys.length,
|
|
214
|
+
});
|
|
215
|
+
} catch (error) {
|
|
216
|
+
logger.warn("Failed to publish backend-signed 14199 snapshot", {
|
|
217
|
+
error: error instanceof Error ? error.message : String(error),
|
|
218
|
+
eventId: ev.id?.substring(0, 12),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Build an unsigned 14199 snapshot event with p-tags for the given pubkeys.
|
|
225
|
+
*/
|
|
226
|
+
private static buildSnapshotEvent(
|
|
227
|
+
ndk: ReturnType<typeof getNDK>,
|
|
228
|
+
allPubkeys: string[],
|
|
229
|
+
): NDKEvent {
|
|
230
|
+
const ev = new NDKEvent(ndk, {
|
|
231
|
+
kind: NDKKind.ProjectAgentSnapshot,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
for (const pk of allPubkeys) {
|
|
235
|
+
ev.tag(["p", pk]);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return ev;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Fetch existing p-tag pubkeys from the owner's latest 14199 event.
|
|
243
|
+
* Returns an empty array on fetch failure (safe: we only add, never remove).
|
|
244
|
+
*/
|
|
245
|
+
private static async fetchExistingPTags(ownerPubkey: string): Promise<string[]> {
|
|
246
|
+
try {
|
|
247
|
+
const ndk = getNDK();
|
|
248
|
+
const events = await ndk.fetchEvents({
|
|
249
|
+
kinds: [NDKKind.ProjectAgentSnapshot as number],
|
|
250
|
+
authors: [ownerPubkey],
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
if (events.size === 0) {
|
|
254
|
+
return [];
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Get the latest event (highest created_at)
|
|
258
|
+
const latest = Array.from(events).sort(
|
|
259
|
+
(a, b) => (b.created_at ?? 0) - (a.created_at ?? 0),
|
|
260
|
+
)[0];
|
|
261
|
+
|
|
262
|
+
return latest.tags
|
|
263
|
+
.filter((t) => t[0] === "p" && t[1])
|
|
264
|
+
.map((t) => t[1]);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
logger.warn("Failed to fetch existing 14199 event, proceeding with empty p-tags", {
|
|
267
|
+
ownerPubkey: ownerPubkey.substring(0, 12),
|
|
268
|
+
error: error instanceof Error ? error.message : String(error),
|
|
269
|
+
});
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Publishes a kind:0 profile event for an agent
|
|
276
|
+
*/
|
|
277
|
+
static async publishAgentProfile(
|
|
278
|
+
signer: NDKPrivateKeySigner,
|
|
279
|
+
agentName: string,
|
|
280
|
+
agentRole: string,
|
|
281
|
+
projectTitle: string,
|
|
282
|
+
projectEvent: NDKProject,
|
|
283
|
+
agentDefinitionEventId?: string,
|
|
284
|
+
agentMetadata?: {
|
|
285
|
+
description?: string;
|
|
286
|
+
instructions?: string;
|
|
287
|
+
useCriteria?: string;
|
|
288
|
+
},
|
|
289
|
+
whitelistedPubkeys?: string[]
|
|
290
|
+
): Promise<void> {
|
|
291
|
+
let profileEvent: NDKEvent;
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
// Check if there are other agents with the same slug (name) in this project
|
|
295
|
+
// If so, append pubkey prefix for disambiguation
|
|
296
|
+
const projectDTag = projectEvent.dTag;
|
|
297
|
+
let displayName = agentName;
|
|
298
|
+
|
|
299
|
+
if (projectDTag) {
|
|
300
|
+
// Load the agent's slug from storage to check for conflicts
|
|
301
|
+
const agent = await agentStorage.loadAgent(signer.pubkey);
|
|
302
|
+
if (agent && agent.slug) {
|
|
303
|
+
// Check if this slug has conflicts (multiple pubkeys for same slug)
|
|
304
|
+
const conflictingAgent = await agentStorage.getAgentBySlugForProject(
|
|
305
|
+
agent.slug,
|
|
306
|
+
projectDTag
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
// If we found an agent with this slug but it's a different pubkey, there's a conflict
|
|
310
|
+
if (conflictingAgent && conflictingAgent.nsec !== agent.nsec) {
|
|
311
|
+
const otherSigner = new NDKPrivateKeySigner(conflictingAgent.nsec);
|
|
312
|
+
if (otherSigner.pubkey !== signer.pubkey) {
|
|
313
|
+
// Conflict exists - append pubkey prefix to both names
|
|
314
|
+
displayName = `${agentName} (${signer.pubkey.slice(0, 5)})`;
|
|
315
|
+
logger.info("Agent slug conflict detected, adding pubkey prefix to Kind 0 name", {
|
|
316
|
+
slug: agent.slug,
|
|
317
|
+
pubkey: signer.pubkey.substring(0, 8),
|
|
318
|
+
projectDTag,
|
|
319
|
+
displayName,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const avatarUrl = AgentProfilePublisher.buildAvatarUrl(signer.pubkey);
|
|
327
|
+
|
|
328
|
+
const profile = {
|
|
329
|
+
name: displayName,
|
|
330
|
+
description: `${agentRole} agent for ${projectTitle}`,
|
|
331
|
+
picture: avatarUrl,
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
profileEvent = new NDKEvent(getNDK(), {
|
|
335
|
+
kind: 0,
|
|
336
|
+
pubkey: signer.pubkey,
|
|
337
|
+
content: JSON.stringify(profile),
|
|
338
|
+
tags: [],
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Validate projectEvent has required fields before tagging
|
|
342
|
+
// Both pubkey and dTag are required for valid NIP-01 addressable coordinates:
|
|
343
|
+
// Format: <kind>:<pubkey>:<d-tag> (e.g., "31933:abc123:my-project")
|
|
344
|
+
// Note: projectDTag was already declared earlier for conflict detection
|
|
345
|
+
if (!projectEvent.pubkey) {
|
|
346
|
+
logger.warn("Project event missing pubkey, skipping a-tag", {
|
|
347
|
+
agentPubkey: signer.pubkey,
|
|
348
|
+
});
|
|
349
|
+
} else if (!projectDTag) {
|
|
350
|
+
logger.warn("Project event missing d-tag, skipping a-tag", {
|
|
351
|
+
agentPubkey: signer.pubkey,
|
|
352
|
+
projectPubkey: projectEvent.pubkey,
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
// Properly tag the project event (creates an "a" tag for kind:31933)
|
|
356
|
+
// Note: We only tag the CURRENT project. Each project publishes
|
|
357
|
+
// the agent's profile with its own a-tag when the agent boots there.
|
|
358
|
+
// This avoids the multi-owner problem where other projects may have
|
|
359
|
+
// different owner pubkeys that we don't have access to.
|
|
360
|
+
profileEvent.tag(projectEvent.tagReference());
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Add e-tag for the agent definition event if it exists and is valid
|
|
364
|
+
// OR add metadata tags as fallback for agents without a valid event ID
|
|
365
|
+
const trimmedEventId = agentDefinitionEventId?.trim() ?? "";
|
|
366
|
+
const isValidHexEventId = /^[a-f0-9]{64}$/i.test(trimmedEventId);
|
|
367
|
+
|
|
368
|
+
if (isValidHexEventId) {
|
|
369
|
+
profileEvent.tags.push(["e", trimmedEventId]);
|
|
370
|
+
} else {
|
|
371
|
+
// Log warning only if an event ID was provided but is invalid
|
|
372
|
+
if (trimmedEventId !== "") {
|
|
373
|
+
logger.warn(
|
|
374
|
+
"Invalid event ID format for agent definition in profile, using metadata tags instead",
|
|
375
|
+
{
|
|
376
|
+
eventId: agentDefinitionEventId,
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Add metadata tags for agents without a valid NDKAgentDefinition event ID
|
|
382
|
+
if (agentMetadata) {
|
|
383
|
+
if (agentMetadata.description) {
|
|
384
|
+
profileEvent.tags.push(["description", agentMetadata.description]);
|
|
385
|
+
}
|
|
386
|
+
if (agentMetadata.instructions) {
|
|
387
|
+
profileEvent.tags.push(["instructions", agentMetadata.instructions]);
|
|
388
|
+
}
|
|
389
|
+
if (agentMetadata.useCriteria) {
|
|
390
|
+
profileEvent.tags.push(["use-criteria", agentMetadata.useCriteria]);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Add p-tags for all whitelisted pubkeys
|
|
396
|
+
if (whitelistedPubkeys && whitelistedPubkeys.length > 0) {
|
|
397
|
+
for (const pubkey of whitelistedPubkeys) {
|
|
398
|
+
if (pubkey && pubkey !== signer.pubkey) {
|
|
399
|
+
// Don't p-tag self
|
|
400
|
+
profileEvent.tags.push(["p", pubkey]);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Add bot tag
|
|
406
|
+
profileEvent.tags.push(["bot"]);
|
|
407
|
+
|
|
408
|
+
// Add tenex tag
|
|
409
|
+
profileEvent.tags.push(["t", "tenex"]);
|
|
410
|
+
|
|
411
|
+
await profileEvent.sign(signer, { pTags: false });
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
await profileEvent.publish();
|
|
415
|
+
} catch (publishError) {
|
|
416
|
+
logger.warn("Failed to publish agent profile (may already exist)", {
|
|
417
|
+
error: publishError,
|
|
418
|
+
agentName,
|
|
419
|
+
pubkey: signer.pubkey.substring(0, 8),
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Schedule debounced 14199 snapshot publish for this project
|
|
424
|
+
const projectTag = projectEvent.dTag;
|
|
425
|
+
if (projectTag) {
|
|
426
|
+
AgentProfilePublisher.publishProjectAgentSnapshot(projectTag);
|
|
427
|
+
}
|
|
428
|
+
} catch (error) {
|
|
429
|
+
logger.error("Failed to create agent profile", {
|
|
430
|
+
error,
|
|
431
|
+
agentName,
|
|
432
|
+
});
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Publishes a kind:3 contact list for an agent
|
|
439
|
+
* This allows agents to follow other agents in the project and whitelisted pubkeys
|
|
440
|
+
*/
|
|
441
|
+
static async publishContactList(
|
|
442
|
+
signer: NDKPrivateKeySigner,
|
|
443
|
+
contactPubkeys: string[]
|
|
444
|
+
): Promise<void> {
|
|
445
|
+
try {
|
|
446
|
+
// Create a kind:3 event (contact list)
|
|
447
|
+
const contactListEvent = new NDKEvent(getNDK(), {
|
|
448
|
+
kind: 3,
|
|
449
|
+
pubkey: signer.pubkey,
|
|
450
|
+
content: "", // Contact list content is usually empty
|
|
451
|
+
tags: [],
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// Add p-tags for each contact
|
|
455
|
+
for (const pubkey of contactPubkeys) {
|
|
456
|
+
if (pubkey && pubkey !== signer.pubkey) {
|
|
457
|
+
// Don't follow self
|
|
458
|
+
contactListEvent.tags.push(["p", pubkey]);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Sign and publish the contact list
|
|
463
|
+
await contactListEvent.sign(signer, { pTags: false });
|
|
464
|
+
|
|
465
|
+
try {
|
|
466
|
+
await contactListEvent.publish();
|
|
467
|
+
} catch (publishError) {
|
|
468
|
+
logger.warn("Failed to publish contact list (may already exist)", {
|
|
469
|
+
error: publishError,
|
|
470
|
+
agentPubkey: signer.pubkey.substring(0, 8),
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
} catch (error) {
|
|
474
|
+
logger.error("Failed to create contact list", {
|
|
475
|
+
error,
|
|
476
|
+
agentPubkey: signer.pubkey.substring(0, 8),
|
|
477
|
+
});
|
|
478
|
+
// Don't throw - contact list is not critical
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Publishes a kind:0 profile event for the TENEX backend daemon.
|
|
484
|
+
* This identifies the backend as an entity on nostr.
|
|
485
|
+
*
|
|
486
|
+
* @param signer The backend's NDKPrivateKeySigner
|
|
487
|
+
* @param backendName The name for the backend profile (default: "tenex backend")
|
|
488
|
+
* @param whitelistedPubkeys Array of pubkeys to include as contacts
|
|
489
|
+
*/
|
|
490
|
+
static async publishBackendProfile(
|
|
491
|
+
signer: NDKPrivateKeySigner,
|
|
492
|
+
backendName: string = "tenex backend",
|
|
493
|
+
whitelistedPubkeys?: string[]
|
|
494
|
+
): Promise<void> {
|
|
495
|
+
try {
|
|
496
|
+
const avatarUrl = AgentProfilePublisher.buildAvatarUrl(signer.pubkey);
|
|
497
|
+
|
|
498
|
+
const profile = {
|
|
499
|
+
name: backendName,
|
|
500
|
+
description: "TENEX Backend Daemon - Multi-agent orchestration system",
|
|
501
|
+
picture: avatarUrl,
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const profileEvent = new NDKEvent(getNDK(), {
|
|
505
|
+
kind: 0,
|
|
506
|
+
pubkey: signer.pubkey,
|
|
507
|
+
content: JSON.stringify(profile),
|
|
508
|
+
tags: [],
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
// Add p-tags for all whitelisted pubkeys
|
|
512
|
+
if (whitelistedPubkeys && whitelistedPubkeys.length > 0) {
|
|
513
|
+
for (const pubkey of whitelistedPubkeys) {
|
|
514
|
+
if (pubkey && pubkey !== signer.pubkey) {
|
|
515
|
+
profileEvent.tags.push(["p", pubkey]);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Add bot tag to indicate this is an automated system
|
|
521
|
+
profileEvent.tags.push(["bot"]);
|
|
522
|
+
|
|
523
|
+
// Add tenex tag for discoverability
|
|
524
|
+
profileEvent.tags.push(["t", "tenex"]);
|
|
525
|
+
|
|
526
|
+
// Add tenex-backend tag to distinguish from agents
|
|
527
|
+
profileEvent.tags.push(["t", "tenex-backend"]);
|
|
528
|
+
|
|
529
|
+
await profileEvent.sign(signer, { pTags: false });
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
// Publish with timeout - don't block daemon startup if relays are slow
|
|
533
|
+
await Promise.race([
|
|
534
|
+
profileEvent.publish(),
|
|
535
|
+
new Promise((_, reject) =>
|
|
536
|
+
setTimeout(() => reject(new Error("Publish timeout")), AgentProfilePublisher.PUBLISH_TIMEOUT_MS)
|
|
537
|
+
),
|
|
538
|
+
]);
|
|
539
|
+
logger.info("Published TENEX backend profile", {
|
|
540
|
+
pubkey: signer.pubkey.substring(0, 8),
|
|
541
|
+
name: backendName,
|
|
542
|
+
});
|
|
543
|
+
} catch (publishError) {
|
|
544
|
+
logger.warn("Failed to publish backend profile (relay timeout or error)", {
|
|
545
|
+
error: publishError,
|
|
546
|
+
pubkey: signer.pubkey.substring(0, 8),
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
} catch (error) {
|
|
550
|
+
logger.error("Failed to create backend profile", {
|
|
551
|
+
error,
|
|
552
|
+
});
|
|
553
|
+
// Don't throw - backend profile is not critical for operation
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* In-memory cache of last published instruction hash per agent pubkey.
|
|
559
|
+
* Used for deduplication to avoid publishing duplicate kind:0 events
|
|
560
|
+
* when compiled instructions haven't changed.
|
|
561
|
+
*/
|
|
562
|
+
private static lastPublishedInstructionHash: Map<string, string> = new Map();
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Publishes a kind:0 profile event with compiled instructions for an agent.
|
|
566
|
+
* Uses hash-based deduplication to avoid publishing when instructions haven't changed.
|
|
567
|
+
* This is fire-and-forget - failures are logged but don't throw.
|
|
568
|
+
*
|
|
569
|
+
* Callers should use `void AgentProfilePublisher.publishCompiledInstructions(...)` to
|
|
570
|
+
* explicitly indicate the fire-and-forget intent.
|
|
571
|
+
*
|
|
572
|
+
* @param signer The agent's NDKPrivateKeySigner
|
|
573
|
+
* @param compiledInstructions The compiled effective instructions from PromptCompilerService
|
|
574
|
+
* @param agentName The agent's display name
|
|
575
|
+
* @param agentRole The agent's role description
|
|
576
|
+
* @param projectTitle The project title for the profile description
|
|
577
|
+
*/
|
|
578
|
+
static async publishCompiledInstructions(
|
|
579
|
+
signer: NDKPrivateKeySigner,
|
|
580
|
+
compiledInstructions: string,
|
|
581
|
+
agentName: string,
|
|
582
|
+
agentRole: string,
|
|
583
|
+
projectTitle: string
|
|
584
|
+
): Promise<void> {
|
|
585
|
+
try {
|
|
586
|
+
// Hash-based deduplication: skip if instructions haven't changed
|
|
587
|
+
const instructionHash = crypto
|
|
588
|
+
.createHash("sha256")
|
|
589
|
+
.update(compiledInstructions)
|
|
590
|
+
.digest("hex");
|
|
591
|
+
|
|
592
|
+
const lastHash = AgentProfilePublisher.lastPublishedInstructionHash.get(signer.pubkey);
|
|
593
|
+
if (lastHash === instructionHash) {
|
|
594
|
+
logger.debug("Skipping kind:0 publish - compiled instructions unchanged", {
|
|
595
|
+
agentPubkey: signer.pubkey.substring(0, 8),
|
|
596
|
+
});
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const avatarUrl = AgentProfilePublisher.buildAvatarUrl(signer.pubkey);
|
|
601
|
+
|
|
602
|
+
const profile = {
|
|
603
|
+
name: agentName,
|
|
604
|
+
description: `${agentRole} agent for ${projectTitle}`,
|
|
605
|
+
picture: avatarUrl,
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const profileEvent = new NDKEvent(getNDK(), {
|
|
609
|
+
kind: 0,
|
|
610
|
+
pubkey: signer.pubkey,
|
|
611
|
+
content: JSON.stringify(profile),
|
|
612
|
+
tags: [],
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
// Add instruction tag with compiled instructions
|
|
616
|
+
profileEvent.tags.push(["instruction", compiledInstructions]);
|
|
617
|
+
|
|
618
|
+
// Add bot tag
|
|
619
|
+
profileEvent.tags.push(["bot"]);
|
|
620
|
+
|
|
621
|
+
// Add tenex tag for discoverability
|
|
622
|
+
profileEvent.tags.push(["t", "tenex"]);
|
|
623
|
+
|
|
624
|
+
await profileEvent.sign(signer, { pTags: false });
|
|
625
|
+
|
|
626
|
+
try {
|
|
627
|
+
// Publish with timeout to prevent blocking
|
|
628
|
+
await Promise.race([
|
|
629
|
+
profileEvent.publish(),
|
|
630
|
+
new Promise((_, reject) =>
|
|
631
|
+
setTimeout(() => reject(new Error("Publish timeout")), AgentProfilePublisher.PUBLISH_TIMEOUT_MS)
|
|
632
|
+
),
|
|
633
|
+
]);
|
|
634
|
+
|
|
635
|
+
// Update deduplication cache on successful publish
|
|
636
|
+
AgentProfilePublisher.lastPublishedInstructionHash.set(signer.pubkey, instructionHash);
|
|
637
|
+
|
|
638
|
+
logger.info("Published kind:0 with compiled instructions", {
|
|
639
|
+
agentPubkey: signer.pubkey.substring(0, 8),
|
|
640
|
+
agentName,
|
|
641
|
+
instructionsLength: compiledInstructions.length,
|
|
642
|
+
});
|
|
643
|
+
} catch (publishError) {
|
|
644
|
+
logger.warn("Failed to publish kind:0 with compiled instructions (relay timeout or error)", {
|
|
645
|
+
error: publishError,
|
|
646
|
+
agentPubkey: signer.pubkey.substring(0, 8),
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
} catch (error) {
|
|
650
|
+
logger.error("Failed to create kind:0 with compiled instructions", {
|
|
651
|
+
error,
|
|
652
|
+
agentPubkey: signer.pubkey.substring(0, 8),
|
|
653
|
+
});
|
|
654
|
+
// Don't throw - this is fire-and-forget
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|