@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,701 @@
|
|
|
1
|
+
import type { StoredAgent } from "@/agents/AgentStorage";
|
|
2
|
+
import { agentStorage } from "@/agents/AgentStorage";
|
|
3
|
+
import { NDKAgentDefinition } from "@/events/NDKAgentDefinition";
|
|
4
|
+
import { logger } from "@/utils/logger";
|
|
5
|
+
import type NDK from "@nostr-dev-kit/ndk";
|
|
6
|
+
import type { NDKEvent, NDKFilter, NDKSubscription } from "@nostr-dev-kit/ndk";
|
|
7
|
+
import { NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
|
8
|
+
import type { ProjectRuntime } from "@/daemon/ProjectRuntime";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Callback to get all active runtimes for reloading agents.
|
|
12
|
+
* Injected by the Daemon to avoid circular dependencies.
|
|
13
|
+
*/
|
|
14
|
+
export type ActiveRuntimesProvider = () => Map<string, ProjectRuntime>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Configuration for the AgentDefinitionMonitor.
|
|
18
|
+
*/
|
|
19
|
+
export interface AgentDefinitionMonitorConfig {
|
|
20
|
+
/** Pubkeys authorized to publish definition upgrades (in addition to original author) */
|
|
21
|
+
whitelistedPubkeys: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Tracks which agents are being monitored, keyed by "definitionDTag:definitionAuthor".
|
|
26
|
+
*/
|
|
27
|
+
interface MonitoredAgent {
|
|
28
|
+
pubkey: string;
|
|
29
|
+
slug: string;
|
|
30
|
+
definitionDTag: string;
|
|
31
|
+
definitionAuthor: string;
|
|
32
|
+
currentEventId?: string;
|
|
33
|
+
currentCreatedAt?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* AgentDefinitionMonitor - Watches for updated agent definition events (kind:4199)
|
|
38
|
+
* and auto-upgrades active agents when newer definitions are published.
|
|
39
|
+
*
|
|
40
|
+
* ## How It Works
|
|
41
|
+
* 1. On startup, scans all installed agents (regardless of active/inactive status)
|
|
42
|
+
* for those with `definitionDTag` + `definitionAuthor` metadata
|
|
43
|
+
* 2. Creates an NDK subscription for kind:4199 events matching those d-tags
|
|
44
|
+
* 3. When a new event arrives:
|
|
45
|
+
* - Verifies the author is authorized (original author or whitelisted)
|
|
46
|
+
* - Checks the event is actually newer (by `created_at` timestamp)
|
|
47
|
+
* - Updates the StoredAgent IN-PLACE (preserving nsec, slug, pmOverrides, etc.)
|
|
48
|
+
* - Reloads the agent in all active project registries
|
|
49
|
+
* 4. Applies a 5000ms debounce to batch events during initial subscription catch-up
|
|
50
|
+
*
|
|
51
|
+
* ## Identity Preservation
|
|
52
|
+
* The following fields are NEVER overwritten during an upgrade:
|
|
53
|
+
* - nsec (agent's private key / identity)
|
|
54
|
+
* - slug (agent's identifier in projects)
|
|
55
|
+
* - pmOverrides (PM designations)
|
|
56
|
+
* - isPM (global PM flag)
|
|
57
|
+
* - projectOverrides (per-project config)
|
|
58
|
+
* - status (active/inactive)
|
|
59
|
+
*
|
|
60
|
+
* ## Backward Compatibility
|
|
61
|
+
* Agents without `definitionDTag` are silently ignored. They can be upgraded
|
|
62
|
+
* by re-installing them, which will set the tracking fields.
|
|
63
|
+
*
|
|
64
|
+
* @see AgentStorage for persistence
|
|
65
|
+
* @see AgentRegistry.reloadAgent for runtime refresh
|
|
66
|
+
*/
|
|
67
|
+
export class AgentDefinitionMonitor {
|
|
68
|
+
private ndk: NDK;
|
|
69
|
+
private config: AgentDefinitionMonitorConfig;
|
|
70
|
+
private getActiveRuntimes: ActiveRuntimesProvider;
|
|
71
|
+
|
|
72
|
+
private subscription: NDKSubscription | null = null;
|
|
73
|
+
private monitoredAgents = new Map<string, MonitoredAgent>(); // key: "dTag:author"
|
|
74
|
+
private isRunning = false;
|
|
75
|
+
|
|
76
|
+
/** Debounce state */
|
|
77
|
+
private pendingEvents = new Map<string, NDKEvent>(); // key: "dTag:author" -> latest event
|
|
78
|
+
private debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
79
|
+
private static readonly DEBOUNCE_MS = 5000;
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
ndk: NDK,
|
|
83
|
+
config: AgentDefinitionMonitorConfig,
|
|
84
|
+
getActiveRuntimes: ActiveRuntimesProvider,
|
|
85
|
+
) {
|
|
86
|
+
this.ndk = ndk;
|
|
87
|
+
this.config = config;
|
|
88
|
+
this.getActiveRuntimes = getActiveRuntimes;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Start monitoring for agent definition updates.
|
|
93
|
+
* Scans all stored agents and subscribes to relevant kind:4199 events.
|
|
94
|
+
*/
|
|
95
|
+
async start(): Promise<void> {
|
|
96
|
+
if (this.isRunning) {
|
|
97
|
+
logger.warn("[AgentDefinitionMonitor] Already running");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
logger.info("[AgentDefinitionMonitor] Starting agent definition monitor");
|
|
102
|
+
|
|
103
|
+
// Collect all monitored agents from storage
|
|
104
|
+
await this.collectMonitoredAgents();
|
|
105
|
+
|
|
106
|
+
if (this.monitoredAgents.size === 0) {
|
|
107
|
+
logger.info("[AgentDefinitionMonitor] No agents with definition tracking found, monitor idle");
|
|
108
|
+
this.isRunning = true;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Subscribe to kind:4199 events for the monitored d-tags
|
|
113
|
+
this.subscribe();
|
|
114
|
+
this.isRunning = true;
|
|
115
|
+
|
|
116
|
+
logger.info("[AgentDefinitionMonitor] Started", {
|
|
117
|
+
monitoredAgents: this.monitoredAgents.size,
|
|
118
|
+
dTags: Array.from(this.monitoredAgents.values()).map(a => a.definitionDTag),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Stop monitoring and clean up.
|
|
124
|
+
*/
|
|
125
|
+
stop(): void {
|
|
126
|
+
if (this.debounceTimer) {
|
|
127
|
+
clearTimeout(this.debounceTimer);
|
|
128
|
+
this.debounceTimer = null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (this.subscription) {
|
|
132
|
+
this.subscription.stop();
|
|
133
|
+
this.subscription = null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.pendingEvents.clear();
|
|
137
|
+
this.monitoredAgents.clear();
|
|
138
|
+
this.isRunning = false;
|
|
139
|
+
|
|
140
|
+
logger.info("[AgentDefinitionMonitor] Stopped");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Refresh the monitored agent list and resubscribe.
|
|
145
|
+
* Call this when new agents are installed that have definition tracking.
|
|
146
|
+
*/
|
|
147
|
+
async refresh(): Promise<void> {
|
|
148
|
+
if (!this.isRunning) return;
|
|
149
|
+
|
|
150
|
+
logger.debug("[AgentDefinitionMonitor] Refreshing monitored agent list");
|
|
151
|
+
|
|
152
|
+
// Clear stale pending state from the previous subscription
|
|
153
|
+
if (this.debounceTimer) {
|
|
154
|
+
clearTimeout(this.debounceTimer);
|
|
155
|
+
this.debounceTimer = null;
|
|
156
|
+
}
|
|
157
|
+
this.pendingEvents.clear();
|
|
158
|
+
|
|
159
|
+
// Stop existing subscription
|
|
160
|
+
if (this.subscription) {
|
|
161
|
+
this.subscription.stop();
|
|
162
|
+
this.subscription = null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Recollect and resubscribe
|
|
166
|
+
await this.collectMonitoredAgents();
|
|
167
|
+
|
|
168
|
+
if (this.monitoredAgents.size > 0) {
|
|
169
|
+
this.subscribe();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
logger.info("[AgentDefinitionMonitor] Refreshed", {
|
|
173
|
+
monitoredAgents: this.monitoredAgents.size,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Scan all stored agents and collect those with definition tracking metadata.
|
|
179
|
+
* Runs bootstrapLegacyAgents first to migrate agents missing definitionDTag/definitionAuthor.
|
|
180
|
+
*/
|
|
181
|
+
private async collectMonitoredAgents(): Promise<void> {
|
|
182
|
+
this.monitoredAgents.clear();
|
|
183
|
+
|
|
184
|
+
const allAgents = await agentStorage.getAllAgents();
|
|
185
|
+
|
|
186
|
+
// Migrate legacy agents before the main loop so they can be monitored
|
|
187
|
+
await this.bootstrapLegacyAgents(allAgents);
|
|
188
|
+
|
|
189
|
+
for (const agent of allAgents) {
|
|
190
|
+
if (!agent.definitionDTag || !agent.definitionAuthor) {
|
|
191
|
+
logger.debug("[AgentDefinitionMonitor] Skipping agent without definition tracking metadata", {
|
|
192
|
+
slug: agent.slug,
|
|
193
|
+
hasEventId: !!agent.eventId,
|
|
194
|
+
hasDTag: !!agent.definitionDTag,
|
|
195
|
+
hasAuthor: !!agent.definitionAuthor,
|
|
196
|
+
});
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
// Derive pubkey from nsec for the agent
|
|
202
|
+
const signer = new NDKPrivateKeySigner(agent.nsec);
|
|
203
|
+
const pubkey = signer.pubkey;
|
|
204
|
+
|
|
205
|
+
const key = this.buildMonitorKey(agent.definitionDTag, agent.definitionAuthor);
|
|
206
|
+
this.monitoredAgents.set(key, {
|
|
207
|
+
pubkey,
|
|
208
|
+
slug: agent.slug,
|
|
209
|
+
definitionDTag: agent.definitionDTag,
|
|
210
|
+
definitionAuthor: agent.definitionAuthor,
|
|
211
|
+
currentEventId: agent.eventId,
|
|
212
|
+
currentCreatedAt: agent.definitionCreatedAt,
|
|
213
|
+
});
|
|
214
|
+
} catch (error) {
|
|
215
|
+
logger.warn("[AgentDefinitionMonitor] Failed to process agent, skipping", {
|
|
216
|
+
slug: agent.slug,
|
|
217
|
+
definitionDTag: agent.definitionDTag,
|
|
218
|
+
error: error instanceof Error ? error.message : String(error),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Bootstrap legacy agents that were installed before definitionDTag/definitionAuthor existed.
|
|
226
|
+
*
|
|
227
|
+
* For agents missing these fields but having an eventId (meaning they came from a kind:4199 event):
|
|
228
|
+
* - definitionDTag is inferred from the agent's slug (convention enforced by agents_publish)
|
|
229
|
+
* - definitionAuthor is recovered by fetching the original event from relays
|
|
230
|
+
*
|
|
231
|
+
* Mutations are applied directly to the provided agent objects AND persisted to storage.
|
|
232
|
+
* This ensures the main loop in collectMonitoredAgents sees the updated fields immediately.
|
|
233
|
+
*/
|
|
234
|
+
private async bootstrapLegacyAgents(agents: StoredAgent[]): Promise<void> {
|
|
235
|
+
const candidates = agents.filter(
|
|
236
|
+
(a) => a.eventId && (!a.definitionDTag || !a.definitionAuthor),
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
if (candidates.length === 0) return;
|
|
240
|
+
|
|
241
|
+
logger.info("[AgentDefinitionMonitor] Bootstrapping legacy agents", {
|
|
242
|
+
count: candidates.length,
|
|
243
|
+
slugs: candidates.map((a) => a.slug),
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
for (const agent of candidates) {
|
|
247
|
+
try {
|
|
248
|
+
let changed = false;
|
|
249
|
+
|
|
250
|
+
// Infer definitionDTag from slug (convention: d-tag = slug)
|
|
251
|
+
if (!agent.definitionDTag) {
|
|
252
|
+
agent.definitionDTag = agent.slug;
|
|
253
|
+
changed = true;
|
|
254
|
+
logger.info("[AgentDefinitionMonitor] Inferred definitionDTag from slug", {
|
|
255
|
+
slug: agent.slug,
|
|
256
|
+
definitionDTag: agent.definitionDTag,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Recover definitionAuthor by fetching the original event
|
|
261
|
+
if (!agent.definitionAuthor && agent.eventId) {
|
|
262
|
+
try {
|
|
263
|
+
const event = await this.ndk.fetchEvent(agent.eventId);
|
|
264
|
+
if (event) {
|
|
265
|
+
agent.definitionAuthor = event.pubkey;
|
|
266
|
+
// Also backfill definitionCreatedAt if missing
|
|
267
|
+
if (!agent.definitionCreatedAt && event.created_at) {
|
|
268
|
+
agent.definitionCreatedAt = event.created_at;
|
|
269
|
+
}
|
|
270
|
+
changed = true;
|
|
271
|
+
logger.info("[AgentDefinitionMonitor] Recovered definitionAuthor from relay", {
|
|
272
|
+
slug: agent.slug,
|
|
273
|
+
eventId: agent.eventId.substring(0, 12),
|
|
274
|
+
definitionAuthor: agent.definitionAuthor.substring(0, 12),
|
|
275
|
+
});
|
|
276
|
+
} else {
|
|
277
|
+
logger.warn("[AgentDefinitionMonitor] Could not fetch event for legacy agent — skipping author recovery", {
|
|
278
|
+
slug: agent.slug,
|
|
279
|
+
eventId: agent.eventId.substring(0, 12),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
} catch (fetchError) {
|
|
283
|
+
logger.warn("[AgentDefinitionMonitor] Failed to fetch event for legacy agent — skipping author recovery", {
|
|
284
|
+
slug: agent.slug,
|
|
285
|
+
eventId: agent.eventId.substring(0, 12),
|
|
286
|
+
error: fetchError instanceof Error ? fetchError.message : String(fetchError),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (changed) {
|
|
292
|
+
await agentStorage.saveAgent(agent);
|
|
293
|
+
logger.info("[AgentDefinitionMonitor] Migrated legacy agent", {
|
|
294
|
+
slug: agent.slug,
|
|
295
|
+
definitionDTag: agent.definitionDTag,
|
|
296
|
+
definitionAuthor: agent.definitionAuthor?.substring(0, 12) ?? "(unknown)",
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
} catch (error) {
|
|
300
|
+
// Never block startup for a single agent's migration failure
|
|
301
|
+
logger.warn("[AgentDefinitionMonitor] Failed to bootstrap legacy agent, skipping", {
|
|
302
|
+
slug: agent.slug,
|
|
303
|
+
error: error instanceof Error ? error.message : String(error),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Create the NDK subscription for kind:4199 events matching monitored d-tags.
|
|
311
|
+
*/
|
|
312
|
+
private subscribe(): void {
|
|
313
|
+
// Collect unique d-tags
|
|
314
|
+
const dTags = new Set<string>();
|
|
315
|
+
for (const agent of this.monitoredAgents.values()) {
|
|
316
|
+
dTags.add(agent.definitionDTag);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (dTags.size === 0) return;
|
|
320
|
+
|
|
321
|
+
const filter: NDKFilter = {
|
|
322
|
+
kinds: [4199 as number],
|
|
323
|
+
"#d": Array.from(dTags),
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
logger.info("[AgentDefinitionMonitor] Subscribing to definition events", {
|
|
327
|
+
dTagCount: dTags.size,
|
|
328
|
+
dTags: Array.from(dTags),
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
this.subscription = this.ndk.subscribe(filter, {
|
|
332
|
+
closeOnEose: false,
|
|
333
|
+
groupable: false,
|
|
334
|
+
onEvent: (event: NDKEvent) => {
|
|
335
|
+
this.handleIncomingEvent(event);
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Handle an incoming kind:4199 event.
|
|
342
|
+
* Filters unauthorized events before staging to prevent them from
|
|
343
|
+
* resetting the debounce timer and deferring legitimate updates.
|
|
344
|
+
* Applies debounce to batch events during initial catch-up.
|
|
345
|
+
*/
|
|
346
|
+
private handleIncomingEvent(event: NDKEvent): void {
|
|
347
|
+
const dTag = event.tagValue("d");
|
|
348
|
+
if (!dTag) {
|
|
349
|
+
logger.debug("[AgentDefinitionMonitor] Ignoring event without d-tag", {
|
|
350
|
+
eventId: event.id?.substring(0, 12),
|
|
351
|
+
});
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const author = event.pubkey;
|
|
356
|
+
|
|
357
|
+
// Check if this matches a monitored agent (by d-tag, author checked separately)
|
|
358
|
+
const monitoredAgent = this.findMonitoredAgentByDTag(dTag);
|
|
359
|
+
if (!monitoredAgent) {
|
|
360
|
+
logger.debug("[AgentDefinitionMonitor] Event d-tag does not match any monitored agent", {
|
|
361
|
+
dTag,
|
|
362
|
+
author: author?.substring(0, 12),
|
|
363
|
+
});
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Authorization check BEFORE staging — unauthorized events must not
|
|
368
|
+
// reset the debounce timer or displace legitimate pending events
|
|
369
|
+
if (!this.isAuthorized(author, monitoredAgent.definitionAuthor)) {
|
|
370
|
+
logger.debug("[AgentDefinitionMonitor] Ignoring event from unauthorized author", {
|
|
371
|
+
dTag,
|
|
372
|
+
author: author?.substring(0, 12),
|
|
373
|
+
expectedAuthor: monitoredAgent.definitionAuthor.substring(0, 12),
|
|
374
|
+
agentSlug: monitoredAgent.slug,
|
|
375
|
+
});
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const key = this.buildMonitorKey(dTag, author);
|
|
380
|
+
|
|
381
|
+
logger.debug("[AgentDefinitionMonitor] New definition event detected", {
|
|
382
|
+
dTag,
|
|
383
|
+
author: author?.substring(0, 12),
|
|
384
|
+
eventId: event.id?.substring(0, 12),
|
|
385
|
+
createdAt: event.created_at,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// Store in pending events (keep only the latest per key)
|
|
389
|
+
const existingPending = this.pendingEvents.get(key);
|
|
390
|
+
if (existingPending && existingPending.created_at && event.created_at) {
|
|
391
|
+
if (event.created_at <= existingPending.created_at) {
|
|
392
|
+
logger.debug("[AgentDefinitionMonitor] Skipping older pending event", {
|
|
393
|
+
dTag,
|
|
394
|
+
existingCreatedAt: existingPending.created_at,
|
|
395
|
+
newCreatedAt: event.created_at,
|
|
396
|
+
});
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
this.pendingEvents.set(key, event);
|
|
401
|
+
|
|
402
|
+
// Reset debounce timer
|
|
403
|
+
if (this.debounceTimer) {
|
|
404
|
+
clearTimeout(this.debounceTimer);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
this.debounceTimer = setTimeout(() => {
|
|
408
|
+
this.processPendingEvents();
|
|
409
|
+
}, AgentDefinitionMonitor.DEBOUNCE_MS);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Process all pending events after the debounce period.
|
|
414
|
+
*/
|
|
415
|
+
private async processPendingEvents(): Promise<void> {
|
|
416
|
+
const pendingEntries = new Map(this.pendingEvents);
|
|
417
|
+
this.pendingEvents.clear();
|
|
418
|
+
this.debounceTimer = null;
|
|
419
|
+
|
|
420
|
+
logger.info("[AgentDefinitionMonitor] Processing pending definition events", {
|
|
421
|
+
count: pendingEntries.size,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
for (const [key, event] of pendingEntries) {
|
|
425
|
+
try {
|
|
426
|
+
await this.processDefinitionEvent(key, event);
|
|
427
|
+
} catch (error) {
|
|
428
|
+
logger.error("[AgentDefinitionMonitor] Failed to process definition event", {
|
|
429
|
+
key,
|
|
430
|
+
eventId: event.id?.substring(0, 12),
|
|
431
|
+
error: error instanceof Error ? error.message : String(error),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Process a single definition event: validate, check recency, upgrade, reload.
|
|
439
|
+
* Authorization is already verified in handleIncomingEvent before staging.
|
|
440
|
+
*/
|
|
441
|
+
private async processDefinitionEvent(_key: string, event: NDKEvent): Promise<void> {
|
|
442
|
+
const dTag = event.tagValue("d");
|
|
443
|
+
const author = event.pubkey;
|
|
444
|
+
|
|
445
|
+
if (!dTag || !author) {
|
|
446
|
+
logger.warn("[AgentDefinitionMonitor] Event missing d-tag or author, skipping");
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Find the monitored agent by d-tag
|
|
451
|
+
const monitoredAgent = this.findMonitoredAgentByDTag(dTag);
|
|
452
|
+
if (!monitoredAgent) {
|
|
453
|
+
logger.warn("[AgentDefinitionMonitor] No monitored agent found for d-tag", { dTag });
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Skip if this is the exact same event we already have
|
|
458
|
+
if (monitoredAgent.currentEventId === event.id) {
|
|
459
|
+
logger.debug("[AgentDefinitionMonitor] Event is the same as current, skipping", {
|
|
460
|
+
dTag,
|
|
461
|
+
eventId: event.id?.substring(0, 12),
|
|
462
|
+
});
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Load current agent from storage to compare timestamps
|
|
467
|
+
const storedAgent = await agentStorage.loadAgent(monitoredAgent.pubkey);
|
|
468
|
+
if (!storedAgent) {
|
|
469
|
+
logger.warn("[AgentDefinitionMonitor] Monitored agent not found in storage", {
|
|
470
|
+
pubkey: monitoredAgent.pubkey.substring(0, 12),
|
|
471
|
+
slug: monitoredAgent.slug,
|
|
472
|
+
});
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Reject events that are not strictly newer than the stored definition.
|
|
477
|
+
// This prevents out-of-order older events from rolling back fields.
|
|
478
|
+
if (storedAgent.definitionCreatedAt && event.created_at) {
|
|
479
|
+
if (event.created_at <= storedAgent.definitionCreatedAt) {
|
|
480
|
+
logger.debug("[AgentDefinitionMonitor] Skipping event older than current definition", {
|
|
481
|
+
dTag,
|
|
482
|
+
eventId: event.id?.substring(0, 12),
|
|
483
|
+
eventCreatedAt: event.created_at,
|
|
484
|
+
storedCreatedAt: storedAgent.definitionCreatedAt,
|
|
485
|
+
agentSlug: monitoredAgent.slug,
|
|
486
|
+
});
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Apply the upgrade
|
|
492
|
+
await this.upgradeAgent(storedAgent, monitoredAgent, event);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Check if an author is authorized to publish definition updates.
|
|
497
|
+
*/
|
|
498
|
+
private isAuthorized(author: string, originalAuthor: string): boolean {
|
|
499
|
+
// Original author is always authorized
|
|
500
|
+
if (author === originalAuthor) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Check whitelist
|
|
505
|
+
if (this.config.whitelistedPubkeys.includes(author)) {
|
|
506
|
+
logger.debug("[AgentDefinitionMonitor] Author authorized via whitelist", {
|
|
507
|
+
author: author.substring(0, 12),
|
|
508
|
+
});
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Upgrade an agent's definition in-place, preserving identity fields.
|
|
517
|
+
*/
|
|
518
|
+
private async upgradeAgent(
|
|
519
|
+
storedAgent: StoredAgent,
|
|
520
|
+
monitoredAgent: MonitoredAgent,
|
|
521
|
+
event: NDKEvent,
|
|
522
|
+
): Promise<void> {
|
|
523
|
+
const agentDef = NDKAgentDefinition.from(event);
|
|
524
|
+
|
|
525
|
+
// Capture before state for logging
|
|
526
|
+
const beforeState = {
|
|
527
|
+
eventId: storedAgent.eventId?.substring(0, 12),
|
|
528
|
+
name: storedAgent.name,
|
|
529
|
+
role: storedAgent.role,
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
// Track which fields actually change
|
|
533
|
+
const changedFields: string[] = [];
|
|
534
|
+
|
|
535
|
+
// Update definition fields (NEVER touch identity fields)
|
|
536
|
+
const newName = agentDef.title || storedAgent.name;
|
|
537
|
+
if (newName !== storedAgent.name) {
|
|
538
|
+
storedAgent.name = newName;
|
|
539
|
+
changedFields.push("name");
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const newRole = agentDef.role || storedAgent.role;
|
|
543
|
+
if (newRole !== storedAgent.role) {
|
|
544
|
+
storedAgent.role = newRole;
|
|
545
|
+
changedFields.push("role");
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const newDescription = agentDef.description || undefined;
|
|
549
|
+
if (newDescription !== storedAgent.description) {
|
|
550
|
+
storedAgent.description = newDescription;
|
|
551
|
+
changedFields.push("description");
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const newInstructions = agentDef.instructions || undefined;
|
|
555
|
+
if (newInstructions !== storedAgent.instructions) {
|
|
556
|
+
storedAgent.instructions = newInstructions;
|
|
557
|
+
changedFields.push("instructions");
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const newUseCriteria = agentDef.useCriteria || undefined;
|
|
561
|
+
if (newUseCriteria !== storedAgent.useCriteria) {
|
|
562
|
+
storedAgent.useCriteria = newUseCriteria;
|
|
563
|
+
changedFields.push("useCriteria");
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Update tool requirements
|
|
567
|
+
const toolTags = event.tags
|
|
568
|
+
.filter((tag) => tag[0] === "tool" && tag[1])
|
|
569
|
+
.map((tag) => tag[1]);
|
|
570
|
+
const newTools = toolTags.length > 0 ? toolTags : undefined;
|
|
571
|
+
const currentTools = storedAgent.default?.tools;
|
|
572
|
+
if (JSON.stringify(newTools) !== JSON.stringify(currentTools)) {
|
|
573
|
+
if (!storedAgent.default) {
|
|
574
|
+
storedAgent.default = {};
|
|
575
|
+
}
|
|
576
|
+
storedAgent.default.tools = newTools;
|
|
577
|
+
changedFields.push("default.tools");
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Update eventId to the new event
|
|
581
|
+
const oldEventId = storedAgent.eventId;
|
|
582
|
+
storedAgent.eventId = event.id;
|
|
583
|
+
if (oldEventId !== event.id) {
|
|
584
|
+
changedFields.push("eventId");
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Update definitionCreatedAt for future recency checks
|
|
588
|
+
if (event.created_at) {
|
|
589
|
+
const oldCreatedAt = storedAgent.definitionCreatedAt;
|
|
590
|
+
storedAgent.definitionCreatedAt = event.created_at;
|
|
591
|
+
if (oldCreatedAt !== event.created_at) {
|
|
592
|
+
changedFields.push("definitionCreatedAt");
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Update the definition author if the event comes from a different (whitelisted) author
|
|
597
|
+
if (event.pubkey !== storedAgent.definitionAuthor) {
|
|
598
|
+
// Only update definitionAuthor if the event d-tag is from a whitelisted pubkey
|
|
599
|
+
// The original author remains canonical unless explicitly overridden
|
|
600
|
+
changedFields.push("definitionAuthor (noted, not overwritten)");
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (changedFields.length === 0) {
|
|
604
|
+
logger.info("[AgentDefinitionMonitor] No changes detected in definition update", {
|
|
605
|
+
agentSlug: monitoredAgent.slug,
|
|
606
|
+
dTag: monitoredAgent.definitionDTag,
|
|
607
|
+
eventId: event.id?.substring(0, 12),
|
|
608
|
+
});
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Save the updated agent
|
|
613
|
+
await agentStorage.saveAgent(storedAgent);
|
|
614
|
+
|
|
615
|
+
// Update monitored agent tracking
|
|
616
|
+
monitoredAgent.currentEventId = event.id;
|
|
617
|
+
monitoredAgent.currentCreatedAt = event.created_at;
|
|
618
|
+
|
|
619
|
+
// Capture after state for logging
|
|
620
|
+
const afterState = {
|
|
621
|
+
eventId: storedAgent.eventId?.substring(0, 12),
|
|
622
|
+
name: storedAgent.name,
|
|
623
|
+
role: storedAgent.role,
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
logger.info("[AgentDefinitionMonitor] Upgrading agent definition", {
|
|
627
|
+
agentSlug: monitoredAgent.slug,
|
|
628
|
+
agentPubkey: monitoredAgent.pubkey.substring(0, 12),
|
|
629
|
+
dTag: monitoredAgent.definitionDTag,
|
|
630
|
+
beforeState,
|
|
631
|
+
afterState,
|
|
632
|
+
changedFields,
|
|
633
|
+
newEventId: event.id?.substring(0, 12),
|
|
634
|
+
upgradeTimestamp: new Date().toISOString(),
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
// Reload agent in all active runtimes
|
|
638
|
+
await this.reloadAgentInRuntimes(monitoredAgent.pubkey, monitoredAgent.slug);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Reload an agent in all active project runtimes that contain it.
|
|
643
|
+
*/
|
|
644
|
+
private async reloadAgentInRuntimes(pubkey: string, slug: string): Promise<void> {
|
|
645
|
+
const activeRuntimes = this.getActiveRuntimes();
|
|
646
|
+
let reloadCount = 0;
|
|
647
|
+
|
|
648
|
+
for (const [projectId, runtime] of activeRuntimes) {
|
|
649
|
+
const context = runtime.getContext();
|
|
650
|
+
if (!context) continue;
|
|
651
|
+
|
|
652
|
+
const agent = context.getAgentByPubkey(pubkey);
|
|
653
|
+
if (!agent) continue;
|
|
654
|
+
|
|
655
|
+
try {
|
|
656
|
+
await context.agentRegistry.reloadAgent(pubkey);
|
|
657
|
+
reloadCount++;
|
|
658
|
+
|
|
659
|
+
if (context.statusPublisher) {
|
|
660
|
+
await context.statusPublisher.publishImmediately();
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
logger.debug("[AgentDefinitionMonitor] Reloaded agent in runtime", {
|
|
664
|
+
agentSlug: slug,
|
|
665
|
+
projectId,
|
|
666
|
+
});
|
|
667
|
+
} catch (error) {
|
|
668
|
+
logger.error("[AgentDefinitionMonitor] Failed to reload agent in runtime", {
|
|
669
|
+
agentSlug: slug,
|
|
670
|
+
projectId,
|
|
671
|
+
error: error instanceof Error ? error.message : String(error),
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
logger.info("[AgentDefinitionMonitor] Agent reloaded in runtimes", {
|
|
677
|
+
agentSlug: slug,
|
|
678
|
+
reloadCount,
|
|
679
|
+
totalRuntimes: activeRuntimes.size,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Find a monitored agent by its d-tag (may match any author).
|
|
685
|
+
*/
|
|
686
|
+
private findMonitoredAgentByDTag(dTag: string): MonitoredAgent | undefined {
|
|
687
|
+
for (const agent of this.monitoredAgents.values()) {
|
|
688
|
+
if (agent.definitionDTag === dTag) {
|
|
689
|
+
return agent;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return undefined;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Build a unique key for a monitored agent.
|
|
697
|
+
*/
|
|
698
|
+
private buildMonitorKey(dTag: string, author: string): string {
|
|
699
|
+
return `${dTag}:${author}`;
|
|
700
|
+
}
|
|
701
|
+
}
|