@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,437 @@
|
|
|
1
|
+
import type { AgentInstance } from "@/agents/types";
|
|
2
|
+
import { NDKKind } from "@/nostr/kinds";
|
|
3
|
+
import { getNDK } from "@/nostr/ndkClient";
|
|
4
|
+
import { PendingDelegationsRegistry, RALRegistry } from "@/services/ral";
|
|
5
|
+
import { logger } from "@/utils/logger";
|
|
6
|
+
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
7
|
+
import { trace } from "@opentelemetry/api";
|
|
8
|
+
import { AgentEventEncoder } from "./AgentEventEncoder";
|
|
9
|
+
import { injectTraceContext } from "./trace-context";
|
|
10
|
+
import type {
|
|
11
|
+
AskConfig,
|
|
12
|
+
CompletionIntent,
|
|
13
|
+
ConversationIntent,
|
|
14
|
+
DelegateConfig,
|
|
15
|
+
DelegationMarkerIntent,
|
|
16
|
+
ErrorIntent,
|
|
17
|
+
EventContext,
|
|
18
|
+
LessonIntent,
|
|
19
|
+
ToolUseIntent,
|
|
20
|
+
} from "./types";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Comprehensive publisher for all agent-related Nostr events.
|
|
24
|
+
* Handles agent creation, responses, completions, and delegations.
|
|
25
|
+
*/
|
|
26
|
+
export class AgentPublisher {
|
|
27
|
+
private agent: AgentInstance;
|
|
28
|
+
private encoder: AgentEventEncoder;
|
|
29
|
+
|
|
30
|
+
constructor(agent: AgentInstance) {
|
|
31
|
+
this.agent = agent;
|
|
32
|
+
this.encoder = new AgentEventEncoder();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Consume unreported runtime from RAL and enhance context with it.
|
|
37
|
+
* This ensures each published event gets the incremental runtime since last publish.
|
|
38
|
+
*
|
|
39
|
+
* IMPORTANT: Always consumes from RAL to advance lastReportedRuntime, even when
|
|
40
|
+
* explicit llmRuntime is provided. This prevents double-counting on subsequent events.
|
|
41
|
+
*/
|
|
42
|
+
private consumeAndEnhanceContext(context: EventContext): EventContext {
|
|
43
|
+
const ralRegistry = RALRegistry.getInstance();
|
|
44
|
+
|
|
45
|
+
// Always consume to advance lastReportedRuntime (prevents double-counting)
|
|
46
|
+
const unreportedRuntime = ralRegistry.consumeUnreportedRuntime(
|
|
47
|
+
this.agent.pubkey,
|
|
48
|
+
context.conversationId,
|
|
49
|
+
context.ralNumber
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// DEBUG: Temporary logging to diagnose llm-runtime issue
|
|
53
|
+
logger.info("[AgentPublisher.consumeAndEnhanceContext]", {
|
|
54
|
+
agent: this.agent.slug,
|
|
55
|
+
pubkey: this.agent.pubkey.substring(0, 8),
|
|
56
|
+
conv: context.conversationId.substring(0, 8),
|
|
57
|
+
ral: context.ralNumber,
|
|
58
|
+
unreportedMs: unreportedRuntime,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// If context already has llmRuntime set explicitly, use that value
|
|
62
|
+
// (but we still consumed above to advance the counter)
|
|
63
|
+
if (context.llmRuntime !== undefined) {
|
|
64
|
+
return context;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
...context,
|
|
69
|
+
llmRuntime: unreportedRuntime > 0 ? unreportedRuntime : undefined,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Safely publish an event with error handling and comprehensive logging.
|
|
75
|
+
* Throws an error if no relays accept the event.
|
|
76
|
+
*/
|
|
77
|
+
private async safePublish(event: NDKEvent, eventType: string): Promise<void> {
|
|
78
|
+
try {
|
|
79
|
+
const relaySet = await event.publish();
|
|
80
|
+
|
|
81
|
+
// Log relay responses
|
|
82
|
+
const successRelays: string[] = [];
|
|
83
|
+
for (const relay of relaySet) {
|
|
84
|
+
successRelays.push(relay.url);
|
|
85
|
+
logger.debug(`Relay accepted ${eventType} event`, {
|
|
86
|
+
eventId: event.id?.substring(0, 8),
|
|
87
|
+
eventType,
|
|
88
|
+
relayUrl: relay.url,
|
|
89
|
+
agent: this.agent.slug,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Fail explicitly when no relays accept the event
|
|
94
|
+
if (successRelays.length === 0) {
|
|
95
|
+
const error = new Error(
|
|
96
|
+
`Event published to 0 relays - all relays rejected the ${eventType} event`
|
|
97
|
+
);
|
|
98
|
+
logger.error("Event published to 0 relays", {
|
|
99
|
+
eventId: event.id?.substring(0, 8),
|
|
100
|
+
eventType,
|
|
101
|
+
agent: this.agent.slug,
|
|
102
|
+
kind: event.kind,
|
|
103
|
+
contentLength: event.content?.length || 0,
|
|
104
|
+
tagCount: event.tags?.length || 0,
|
|
105
|
+
rawEvent: JSON.stringify(event.rawEvent()),
|
|
106
|
+
});
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
logger.info(`Published ${eventType} event to ${successRelays.length} relay(s)`, {
|
|
111
|
+
eventId: event.id?.substring(0, 8),
|
|
112
|
+
eventType,
|
|
113
|
+
agent: this.agent.slug,
|
|
114
|
+
relays: successRelays,
|
|
115
|
+
});
|
|
116
|
+
} catch (error) {
|
|
117
|
+
logger.error(`Failed to publish ${eventType}`, {
|
|
118
|
+
error,
|
|
119
|
+
eventId: event.id?.substring(0, 8),
|
|
120
|
+
agent: this.agent.slug,
|
|
121
|
+
kind: event.kind,
|
|
122
|
+
contentLength: event.content?.length || 0,
|
|
123
|
+
tagCount: event.tags?.length || 0,
|
|
124
|
+
rawEvent: JSON.stringify(event.rawEvent()),
|
|
125
|
+
});
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Add delegation tag to an event, linking it to the parent conversation.
|
|
132
|
+
* This method is used by delegate() and ask() to establish the parent-child
|
|
133
|
+
* relationship between conversations.
|
|
134
|
+
*
|
|
135
|
+
* @param event - The event to add the delegation tag to
|
|
136
|
+
* @param context - The event context containing the conversationId
|
|
137
|
+
* @throws Error if context.conversationId is missing
|
|
138
|
+
*/
|
|
139
|
+
private addDelegationTag(event: NDKEvent, context: EventContext): void {
|
|
140
|
+
if (!context.conversationId) {
|
|
141
|
+
throw new Error("Cannot add delegation tag: conversationId is required in context for delegation events");
|
|
142
|
+
}
|
|
143
|
+
event.tags.push(["delegation", context.conversationId]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Publish a completion event.
|
|
148
|
+
* Creates and publishes a properly tagged completion event with p-tag.
|
|
149
|
+
* Includes both incremental runtime (llm-runtime) and total runtime (llm-runtime-total).
|
|
150
|
+
*
|
|
151
|
+
* DELEGATION CHAIN ROUTING: For conversations with a delegation chain, the completion
|
|
152
|
+
* is routed to the immediate delegator (second-to-last in chain), not triggeringEvent.pubkey.
|
|
153
|
+
* The recipient pubkey is pre-resolved by createEventContext (layer 3) and passed via
|
|
154
|
+
* context.completionRecipientPubkey. This avoids a layer violation - AgentPublisher (layer 2)
|
|
155
|
+
* cannot import ConversationStore directly.
|
|
156
|
+
*
|
|
157
|
+
* RACE CONDITION GUARD: If this conversation was killed via the kill tool,
|
|
158
|
+
* this method returns undefined instead of publishing. This prevents the scenario
|
|
159
|
+
* where an agent continues running (e.g., in a long tool execution) after being
|
|
160
|
+
* killed and then publishes a completion that triggers the parent to process it.
|
|
161
|
+
*/
|
|
162
|
+
async complete(intent: CompletionIntent, context: EventContext): Promise<NDKEvent | undefined> {
|
|
163
|
+
const ralRegistry = RALRegistry.getInstance();
|
|
164
|
+
|
|
165
|
+
// RACE CONDITION GUARD: Check if this agent+conversation was killed
|
|
166
|
+
// ISSUE 3 FIX: Using agent-scoped check ensures killing one agent
|
|
167
|
+
// doesn't suppress completions for other agents in the same conversation.
|
|
168
|
+
if (ralRegistry.isAgentConversationKilled(this.agent.pubkey, context.conversationId)) {
|
|
169
|
+
logger.warn("[AgentPublisher.complete] Skipping completion - agent+conversation was killed", {
|
|
170
|
+
agent: this.agent.slug,
|
|
171
|
+
agentPubkey: this.agent.pubkey.substring(0, 12),
|
|
172
|
+
conversationId: context.conversationId.substring(0, 12),
|
|
173
|
+
ralNumber: context.ralNumber,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
trace.getActiveSpan()?.addEvent("publisher.completion_skipped_killed", {
|
|
177
|
+
"agent.slug": this.agent.slug,
|
|
178
|
+
"agent.pubkey": this.agent.pubkey.substring(0, 12),
|
|
179
|
+
"conversation.id": context.conversationId.substring(0, 12),
|
|
180
|
+
"ral.number": context.ralNumber,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const enhancedContext = this.consumeAndEnhanceContext(context);
|
|
187
|
+
|
|
188
|
+
// For completion events, include the total accumulated runtime for the entire RAL
|
|
189
|
+
// This allows delegation aggregation to get the correct total runtime
|
|
190
|
+
const totalRuntime = ralRegistry.getAccumulatedRuntime(
|
|
191
|
+
this.agent.pubkey,
|
|
192
|
+
context.conversationId,
|
|
193
|
+
context.ralNumber
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// completionRecipientPubkey is pre-resolved by createEventContext (layer 3)
|
|
197
|
+
// from the delegation chain stored in ConversationStore. This avoids a layer
|
|
198
|
+
// violation - AgentPublisher (layer 2) cannot import ConversationStore directly.
|
|
199
|
+
const contextWithExtras: EventContext = {
|
|
200
|
+
...enhancedContext,
|
|
201
|
+
llmRuntimeTotal: totalRuntime > 0 ? totalRuntime : undefined,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const event = this.encoder.encodeCompletion(intent, contextWithExtras);
|
|
205
|
+
|
|
206
|
+
injectTraceContext(event);
|
|
207
|
+
await this.agent.sign(event);
|
|
208
|
+
await this.safePublish(event, "completion");
|
|
209
|
+
|
|
210
|
+
return event;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Publish a conversation event (mid-loop response without p-tag).
|
|
215
|
+
* Used when agent has text output but delegations are still pending.
|
|
216
|
+
*/
|
|
217
|
+
async conversation(intent: ConversationIntent, context: EventContext): Promise<NDKEvent> {
|
|
218
|
+
const enhancedContext = this.consumeAndEnhanceContext(context);
|
|
219
|
+
const event = this.encoder.encodeConversation(intent, enhancedContext);
|
|
220
|
+
|
|
221
|
+
injectTraceContext(event);
|
|
222
|
+
await this.agent.sign(event);
|
|
223
|
+
await this.safePublish(event, "conversation");
|
|
224
|
+
|
|
225
|
+
return event;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Publish a delegation event
|
|
230
|
+
*/
|
|
231
|
+
async delegate(config: DelegateConfig, context: EventContext): Promise<string> {
|
|
232
|
+
const enhancedContext = this.consumeAndEnhanceContext(context);
|
|
233
|
+
const ndk = getNDK();
|
|
234
|
+
const event = new NDKEvent(ndk);
|
|
235
|
+
event.kind = NDKKind.Text; // kind:1 - unified conversation format
|
|
236
|
+
event.content = config.content;
|
|
237
|
+
|
|
238
|
+
// Add recipient p-tag
|
|
239
|
+
event.tags.push(["p", config.recipient]);
|
|
240
|
+
|
|
241
|
+
// No e-tag: delegation events start separate conversations
|
|
242
|
+
|
|
243
|
+
if (config.branch) {
|
|
244
|
+
event.tags.push(["branch", config.branch]);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Add nudge tags for the delegated agent (deduplicated for robustness)
|
|
248
|
+
if (config.nudges && config.nudges.length > 0) {
|
|
249
|
+
const uniqueNudges = [...new Set(config.nudges)];
|
|
250
|
+
for (const nudgeId of uniqueNudges) {
|
|
251
|
+
event.tags.push(["nudge", nudgeId]);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Add standard metadata (project tag, model, cost, execution time, etc)
|
|
256
|
+
this.encoder.addStandardTags(event, enhancedContext);
|
|
257
|
+
|
|
258
|
+
// Forward branch tag from triggering event if not explicitly set
|
|
259
|
+
if (!config.branch) {
|
|
260
|
+
this.encoder.forwardBranchTag(event, enhancedContext);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Add delegation tag linking to parent conversation
|
|
264
|
+
this.addDelegationTag(event, enhancedContext);
|
|
265
|
+
|
|
266
|
+
injectTraceContext(event);
|
|
267
|
+
await this.agent.sign(event);
|
|
268
|
+
await this.safePublish(event, "delegation");
|
|
269
|
+
|
|
270
|
+
// Register with PendingDelegationsRegistry for q-tag correlation
|
|
271
|
+
PendingDelegationsRegistry.register(this.agent.pubkey, context.conversationId, event.id);
|
|
272
|
+
|
|
273
|
+
return event.id;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Publish an ask event using the multi-question format.
|
|
278
|
+
* Returns the published NDKEvent so callers can create a ConversationStore.
|
|
279
|
+
*/
|
|
280
|
+
async ask(config: AskConfig, context: EventContext): Promise<NDKEvent> {
|
|
281
|
+
const enhancedContext = this.consumeAndEnhanceContext(context);
|
|
282
|
+
const ndk = getNDK();
|
|
283
|
+
const event = new NDKEvent(ndk);
|
|
284
|
+
event.kind = NDKKind.Text; // kind:1 - unified conversation format
|
|
285
|
+
// Content is just the context (user has no access to conversation history)
|
|
286
|
+
event.content = config.context;
|
|
287
|
+
|
|
288
|
+
// Add title tag
|
|
289
|
+
event.tags.push(["title", config.title]);
|
|
290
|
+
|
|
291
|
+
// Add question/multiselect tags
|
|
292
|
+
for (const question of config.questions) {
|
|
293
|
+
if (question.type === "question") {
|
|
294
|
+
const tag = ["question", question.title, question.question];
|
|
295
|
+
if (question.suggestions) {
|
|
296
|
+
tag.push(...question.suggestions);
|
|
297
|
+
}
|
|
298
|
+
event.tags.push(tag);
|
|
299
|
+
} else if (question.type === "multiselect") {
|
|
300
|
+
const tag = ["multiselect", question.title, question.question];
|
|
301
|
+
if (question.options) {
|
|
302
|
+
tag.push(...question.options);
|
|
303
|
+
}
|
|
304
|
+
event.tags.push(tag);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Add recipient p-tag
|
|
309
|
+
event.tags.push(["p", config.recipient]);
|
|
310
|
+
|
|
311
|
+
// No e-tag: ask events start separate conversations (like delegate)
|
|
312
|
+
|
|
313
|
+
// Add standard metadata (project tag, model, cost, execution time, runtime, etc)
|
|
314
|
+
this.encoder.addStandardTags(event, enhancedContext);
|
|
315
|
+
|
|
316
|
+
// Add ask marker
|
|
317
|
+
event.tags.push(["ask", "true"]);
|
|
318
|
+
|
|
319
|
+
// Add t-tag for ask events
|
|
320
|
+
event.tags.push(["t", "ask"]);
|
|
321
|
+
|
|
322
|
+
// Add delegation tag linking to parent conversation
|
|
323
|
+
this.addDelegationTag(event, enhancedContext);
|
|
324
|
+
|
|
325
|
+
injectTraceContext(event);
|
|
326
|
+
await this.agent.sign(event);
|
|
327
|
+
await this.safePublish(event, "ask");
|
|
328
|
+
|
|
329
|
+
// Register with PendingDelegationsRegistry for q-tag correlation
|
|
330
|
+
PendingDelegationsRegistry.register(this.agent.pubkey, enhancedContext.conversationId, event.id);
|
|
331
|
+
|
|
332
|
+
return event;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Publish a delegation follow-up event
|
|
337
|
+
*/
|
|
338
|
+
async delegateFollowup(
|
|
339
|
+
params: {
|
|
340
|
+
recipient: string;
|
|
341
|
+
content: string;
|
|
342
|
+
delegationEventId: string;
|
|
343
|
+
replyToEventId?: string;
|
|
344
|
+
},
|
|
345
|
+
context: EventContext
|
|
346
|
+
): Promise<string> {
|
|
347
|
+
const enhancedContext = this.consumeAndEnhanceContext(context);
|
|
348
|
+
const ndk = getNDK();
|
|
349
|
+
const event = new NDKEvent(ndk);
|
|
350
|
+
event.kind = NDKKind.Text; // kind:1 - unified conversation format
|
|
351
|
+
event.content = params.content;
|
|
352
|
+
|
|
353
|
+
// Add recipient p-tag
|
|
354
|
+
event.tags.push(["p", params.recipient]);
|
|
355
|
+
|
|
356
|
+
// Add reference to the original delegation event
|
|
357
|
+
event.tags.push(["e", params.delegationEventId]);
|
|
358
|
+
|
|
359
|
+
// Reply to specific response event if provided (for threading)
|
|
360
|
+
if (params.replyToEventId) {
|
|
361
|
+
event.tags.push(["e", params.replyToEventId]);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Add standard metadata (project tag, model, cost, execution time, runtime, etc)
|
|
365
|
+
this.encoder.addStandardTags(event, enhancedContext);
|
|
366
|
+
|
|
367
|
+
// Forward branch tag from triggering event
|
|
368
|
+
this.encoder.forwardBranchTag(event, enhancedContext);
|
|
369
|
+
|
|
370
|
+
injectTraceContext(event);
|
|
371
|
+
await this.agent.sign(event);
|
|
372
|
+
await this.safePublish(event, "followup");
|
|
373
|
+
|
|
374
|
+
return event.id;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Publish an error event.
|
|
379
|
+
* Creates and publishes an error notification event.
|
|
380
|
+
*/
|
|
381
|
+
async error(intent: ErrorIntent, context: EventContext): Promise<NDKEvent> {
|
|
382
|
+
const enhancedContext = this.consumeAndEnhanceContext(context);
|
|
383
|
+
const event = this.encoder.encodeError(intent, enhancedContext);
|
|
384
|
+
|
|
385
|
+
injectTraceContext(event);
|
|
386
|
+
await this.agent.sign(event);
|
|
387
|
+
await this.safePublish(event, "error");
|
|
388
|
+
|
|
389
|
+
return event;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Publish a lesson learned event.
|
|
394
|
+
*/
|
|
395
|
+
async lesson(intent: LessonIntent, context: EventContext): Promise<NDKEvent> {
|
|
396
|
+
const enhancedContext = this.consumeAndEnhanceContext(context);
|
|
397
|
+
const lessonEvent = this.encoder.encodeLesson(intent, enhancedContext, this.agent);
|
|
398
|
+
|
|
399
|
+
injectTraceContext(lessonEvent);
|
|
400
|
+
await this.agent.sign(lessonEvent);
|
|
401
|
+
await this.safePublish(lessonEvent, "lesson");
|
|
402
|
+
|
|
403
|
+
return lessonEvent;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Publish a tool usage event.
|
|
408
|
+
* Creates and publishes an event with tool name and output tags.
|
|
409
|
+
*/
|
|
410
|
+
async toolUse(intent: ToolUseIntent, context: EventContext): Promise<NDKEvent> {
|
|
411
|
+
const enhancedContext = this.consumeAndEnhanceContext(context);
|
|
412
|
+
const event = this.encoder.encodeToolUse(intent, enhancedContext);
|
|
413
|
+
|
|
414
|
+
injectTraceContext(event);
|
|
415
|
+
await this.agent.sign(event);
|
|
416
|
+
await this.safePublish(event, `tool:${intent.toolName}`);
|
|
417
|
+
|
|
418
|
+
return event;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Publish a delegation marker event.
|
|
423
|
+
* Delegation markers track the lifecycle of delegation conversations.
|
|
424
|
+
*
|
|
425
|
+
* Note: This does NOT consume runtime from RAL since markers are not part of
|
|
426
|
+
* the agent's reasoning/action loop. They are metadata events for tracking.
|
|
427
|
+
*/
|
|
428
|
+
async delegationMarker(intent: DelegationMarkerIntent): Promise<NDKEvent> {
|
|
429
|
+
const event = this.encoder.encodeDelegationMarker(intent);
|
|
430
|
+
|
|
431
|
+
injectTraceContext(event);
|
|
432
|
+
await this.agent.sign(event);
|
|
433
|
+
await this.safePublish(event, `delegation-marker:${intent.status}`);
|
|
434
|
+
|
|
435
|
+
return event;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlossomService - Handles Blossom media uploads with Nostr authentication
|
|
3
|
+
*
|
|
4
|
+
* This service encapsulates all Blossom upload logic including:
|
|
5
|
+
* - SHA256 hash calculation
|
|
6
|
+
* - Kind 24242 authorization event creation and signing
|
|
7
|
+
* - HTTP upload to Blossom servers
|
|
8
|
+
*
|
|
9
|
+
* Centralizes NDK usage in the nostr layer, allowing tools to delegate
|
|
10
|
+
* Blossom operations without directly importing NDK.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as crypto from "node:crypto";
|
|
14
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
15
|
+
import { logger } from "@/utils/logger";
|
|
16
|
+
import { NDKEvent as NDKEventClass } from "@nostr-dev-kit/ndk";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Minimal signer interface needed for Blossom authentication.
|
|
20
|
+
* This allows both AgentInstance and ToolAgentInfo to be used.
|
|
21
|
+
*/
|
|
22
|
+
export interface BlossomSigner {
|
|
23
|
+
sign(event: NDKEvent): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const UPLOAD_TIMEOUT_MS = 60_000;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Result of a successful Blossom upload
|
|
30
|
+
*/
|
|
31
|
+
export interface BlossomUploadResult {
|
|
32
|
+
/** URL of the uploaded blob */
|
|
33
|
+
url: string;
|
|
34
|
+
/** SHA256 hash of the uploaded data */
|
|
35
|
+
sha256: string;
|
|
36
|
+
/** Size in bytes */
|
|
37
|
+
size: number;
|
|
38
|
+
/** MIME type (if returned by server) */
|
|
39
|
+
type?: string;
|
|
40
|
+
/** Timestamp of upload */
|
|
41
|
+
uploaded?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Options for uploading to Blossom
|
|
46
|
+
*/
|
|
47
|
+
export interface BlossomUploadOptions {
|
|
48
|
+
/** Blossom server URL (required - callers must load from config) */
|
|
49
|
+
serverUrl: string;
|
|
50
|
+
/** Description for the authorization event content */
|
|
51
|
+
description?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* MIME type to file extension mapping
|
|
56
|
+
*/
|
|
57
|
+
const MIME_TO_EXTENSION: Record<string, string> = {
|
|
58
|
+
"image/jpeg": ".jpg",
|
|
59
|
+
"image/png": ".png",
|
|
60
|
+
"image/gif": ".gif",
|
|
61
|
+
"image/webp": ".webp",
|
|
62
|
+
"video/mp4": ".mp4",
|
|
63
|
+
"video/quicktime": ".mov",
|
|
64
|
+
"video/x-msvideo": ".avi",
|
|
65
|
+
"video/webm": ".webm",
|
|
66
|
+
"audio/mpeg": ".mp3",
|
|
67
|
+
"audio/wav": ".wav",
|
|
68
|
+
"application/pdf": ".pdf",
|
|
69
|
+
"application/json": ".json",
|
|
70
|
+
"text/plain": ".txt",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get file extension from MIME type
|
|
75
|
+
*/
|
|
76
|
+
export function getExtensionFromMimeType(mimeType: string): string {
|
|
77
|
+
return MIME_TO_EXTENSION[mimeType] || "";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Calculate SHA256 hash of data
|
|
82
|
+
*/
|
|
83
|
+
export function calculateSHA256(data: Buffer): string {
|
|
84
|
+
return crypto.createHash("sha256").update(data).digest("hex");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* BlossomService handles uploading media to Blossom servers with Nostr authentication.
|
|
89
|
+
*
|
|
90
|
+
* This service:
|
|
91
|
+
* 1. Calculates SHA256 hash of the data
|
|
92
|
+
* 2. Creates and signs a kind 24242 authorization event
|
|
93
|
+
* 3. Uploads the data to the Blossom server
|
|
94
|
+
* 4. Returns the result with URL and metadata
|
|
95
|
+
*/
|
|
96
|
+
export class BlossomService {
|
|
97
|
+
private signer: BlossomSigner;
|
|
98
|
+
|
|
99
|
+
constructor(signer: BlossomSigner) {
|
|
100
|
+
this.signer = signer;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Upload data to Blossom server with Nostr authentication.
|
|
105
|
+
*
|
|
106
|
+
* @param data - The binary data to upload
|
|
107
|
+
* @param mimeType - MIME type of the data (e.g., "image/png")
|
|
108
|
+
* @param options - Upload configuration including serverUrl (required)
|
|
109
|
+
* @returns Upload result with URL and metadata
|
|
110
|
+
*/
|
|
111
|
+
async upload(
|
|
112
|
+
data: Buffer,
|
|
113
|
+
mimeType: string,
|
|
114
|
+
options: BlossomUploadOptions
|
|
115
|
+
): Promise<BlossomUploadResult> {
|
|
116
|
+
const { serverUrl, description = "Blossom upload" } = options;
|
|
117
|
+
|
|
118
|
+
// Calculate hash
|
|
119
|
+
const sha256Hash = calculateSHA256(data);
|
|
120
|
+
|
|
121
|
+
logger.debug("[BlossomService] Preparing upload", {
|
|
122
|
+
size: data.length,
|
|
123
|
+
mimeType,
|
|
124
|
+
sha256: sha256Hash.slice(0, 12) + "...",
|
|
125
|
+
serverUrl,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Create and sign authorization event
|
|
129
|
+
const authEvent = await this.createAuthEvent(sha256Hash, description);
|
|
130
|
+
|
|
131
|
+
// Upload to server
|
|
132
|
+
const result = await this.uploadToServer(serverUrl, data, mimeType, authEvent);
|
|
133
|
+
|
|
134
|
+
logger.info("[BlossomService] Upload successful", {
|
|
135
|
+
url: result.url,
|
|
136
|
+
sha256: result.sha256,
|
|
137
|
+
size: result.size,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Create Blossom authorization event (kind 24242)
|
|
145
|
+
*
|
|
146
|
+
* Per Blossom protocol, this event authorizes the upload:
|
|
147
|
+
* - kind: 24242
|
|
148
|
+
* - content: description of the upload
|
|
149
|
+
* - tags: ["t", "upload"], ["x", sha256], ["expiration", timestamp]
|
|
150
|
+
*/
|
|
151
|
+
private async createAuthEvent(sha256Hash: string, description: string): Promise<NDKEvent> {
|
|
152
|
+
const event = new NDKEventClass();
|
|
153
|
+
event.kind = 24242;
|
|
154
|
+
event.content = description;
|
|
155
|
+
event.created_at = Math.floor(Date.now() / 1000);
|
|
156
|
+
event.tags = [
|
|
157
|
+
["t", "upload"],
|
|
158
|
+
["x", sha256Hash],
|
|
159
|
+
["expiration", String(Math.floor(Date.now() / 1000) + 3600)], // 1 hour expiration
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
await this.signer.sign(event);
|
|
163
|
+
return event;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Upload data to Blossom server
|
|
168
|
+
*/
|
|
169
|
+
private async uploadToServer(
|
|
170
|
+
serverUrl: string,
|
|
171
|
+
data: Buffer,
|
|
172
|
+
mimeType: string,
|
|
173
|
+
authEvent: NDKEvent
|
|
174
|
+
): Promise<BlossomUploadResult> {
|
|
175
|
+
// Encode the auth event as base64 for the Authorization header
|
|
176
|
+
const authHeader = `Nostr ${Buffer.from(JSON.stringify(authEvent.rawEvent())).toString("base64")}`;
|
|
177
|
+
|
|
178
|
+
const controller = new AbortController();
|
|
179
|
+
const timeout = setTimeout(() => controller.abort(), UPLOAD_TIMEOUT_MS);
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const response = await fetch(`${serverUrl}/upload`, {
|
|
183
|
+
method: "PUT",
|
|
184
|
+
headers: {
|
|
185
|
+
Authorization: authHeader,
|
|
186
|
+
"Content-Type": mimeType,
|
|
187
|
+
"Content-Length": String(data.length),
|
|
188
|
+
},
|
|
189
|
+
body: data,
|
|
190
|
+
signal: controller.signal,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
let errorMessage = `Upload failed with status ${response.status}`;
|
|
195
|
+
try {
|
|
196
|
+
const errorData = (await response.json()) as { message?: string };
|
|
197
|
+
if (errorData.message) {
|
|
198
|
+
errorMessage = `Upload failed: ${errorData.message}`;
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
// If parsing JSON fails, use the default error message
|
|
202
|
+
}
|
|
203
|
+
throw new Error(errorMessage);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const result = (await response.json()) as BlossomUploadResult;
|
|
207
|
+
|
|
208
|
+
// Add extension to URL if not present
|
|
209
|
+
if (result.url && !result.url.match(/\.\w+$/)) {
|
|
210
|
+
const ext = getExtensionFromMimeType(mimeType);
|
|
211
|
+
if (ext) {
|
|
212
|
+
result.url = result.url + ext;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return result;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
219
|
+
throw new Error(`Upload timed out after ${UPLOAD_TIMEOUT_MS}ms`, { cause: error });
|
|
220
|
+
}
|
|
221
|
+
throw error;
|
|
222
|
+
} finally {
|
|
223
|
+
clearTimeout(timeout);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|