@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,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CooldownRegistry - Tracks aborted conversation:agent tuples to prevent immediate re-routing
|
|
3
|
+
*
|
|
4
|
+
* After an agent is aborted in a conversation, this registry enforces a 15-second cooldown
|
|
5
|
+
* period to prevent the dispatch service from routing new messages to that agent in that
|
|
6
|
+
* conversation. This prevents race conditions where an abort signal crosses with new messages.
|
|
7
|
+
*
|
|
8
|
+
* Key design:
|
|
9
|
+
* - Tuple-based: Cooldown is specific to (conversationId, agentPubkey) pair
|
|
10
|
+
* - Time-based expiry: Tuples expire after COOLDOWN_DURATION_MS
|
|
11
|
+
* - Automatic cleanup: Expired entries are periodically removed
|
|
12
|
+
* - OTL traces: Emits trace events when routing is blocked
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { logger } from "@/utils/logger";
|
|
16
|
+
import { shortenConversationId } from "@/utils/conversation-id";
|
|
17
|
+
import { trace } from "@opentelemetry/api";
|
|
18
|
+
|
|
19
|
+
/** Cooldown duration in milliseconds (15 seconds) */
|
|
20
|
+
const COOLDOWN_DURATION_MS = 15000;
|
|
21
|
+
|
|
22
|
+
/** Cleanup interval for expired entries (30 seconds) */
|
|
23
|
+
const CLEANUP_INTERVAL_MS = 30000;
|
|
24
|
+
|
|
25
|
+
interface CooldownEntry {
|
|
26
|
+
projectId: string;
|
|
27
|
+
conversationId: string;
|
|
28
|
+
agentPubkey: string;
|
|
29
|
+
abortedAt: number;
|
|
30
|
+
reason?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class CooldownRegistry {
|
|
34
|
+
private static instance: CooldownRegistry;
|
|
35
|
+
|
|
36
|
+
/** Map from "projectId:conversationId:agentPubkey" to cooldown entry */
|
|
37
|
+
private cooldowns: Map<string, CooldownEntry> = new Map();
|
|
38
|
+
|
|
39
|
+
/** Cleanup interval handle */
|
|
40
|
+
private cleanupInterval: ReturnType<typeof setInterval> | null = null;
|
|
41
|
+
|
|
42
|
+
private constructor() {
|
|
43
|
+
this.startCleanupInterval();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static getInstance(): CooldownRegistry {
|
|
47
|
+
if (!CooldownRegistry.instance) {
|
|
48
|
+
CooldownRegistry.instance = new CooldownRegistry();
|
|
49
|
+
}
|
|
50
|
+
return CooldownRegistry.instance;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Start periodic cleanup of expired cooldown entries
|
|
55
|
+
*/
|
|
56
|
+
private startCleanupInterval(): void {
|
|
57
|
+
this.cleanupInterval = setInterval(() => {
|
|
58
|
+
this.cleanupExpiredEntries();
|
|
59
|
+
}, CLEANUP_INTERVAL_MS);
|
|
60
|
+
this.cleanupInterval.unref();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Remove expired cooldown entries
|
|
65
|
+
*/
|
|
66
|
+
private cleanupExpiredEntries(): void {
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
let cleanedCount = 0;
|
|
69
|
+
|
|
70
|
+
for (const [key, entry] of this.cooldowns.entries()) {
|
|
71
|
+
if (now - entry.abortedAt > COOLDOWN_DURATION_MS) {
|
|
72
|
+
this.cooldowns.delete(key);
|
|
73
|
+
cleanedCount++;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (cleanedCount > 0) {
|
|
78
|
+
trace.getActiveSpan()?.addEvent("cooldown.cleanup_expired", {
|
|
79
|
+
"cooldown.cleaned_count": cleanedCount,
|
|
80
|
+
"cooldown.remaining_count": this.cooldowns.size,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Make a unique key for a project:conversation:agent tuple
|
|
87
|
+
*/
|
|
88
|
+
private makeKey(projectId: string, conversationId: string, agentPubkey: string): string {
|
|
89
|
+
return `${projectId}:${conversationId}:${agentPubkey}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Add a project:conversation:agent tuple to the cooldown registry
|
|
94
|
+
*
|
|
95
|
+
* @param projectId - The project ID
|
|
96
|
+
* @param conversationId - The conversation ID
|
|
97
|
+
* @param agentPubkey - The agent's pubkey
|
|
98
|
+
* @param reason - Optional reason for the abort (for debugging)
|
|
99
|
+
*/
|
|
100
|
+
add(projectId: string, conversationId: string, agentPubkey: string, reason?: string): void {
|
|
101
|
+
const key = this.makeKey(projectId, conversationId, agentPubkey);
|
|
102
|
+
const entry: CooldownEntry = {
|
|
103
|
+
projectId,
|
|
104
|
+
conversationId,
|
|
105
|
+
agentPubkey,
|
|
106
|
+
abortedAt: Date.now(),
|
|
107
|
+
reason,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
this.cooldowns.set(key, entry);
|
|
111
|
+
|
|
112
|
+
trace.getActiveSpan()?.addEvent("cooldown.added", {
|
|
113
|
+
"cooldown.project_id": projectId.substring(0, 12),
|
|
114
|
+
"cooldown.conversation_id": shortenConversationId(conversationId),
|
|
115
|
+
"cooldown.agent_pubkey": agentPubkey.substring(0, 12),
|
|
116
|
+
"cooldown.reason": reason ?? "unknown",
|
|
117
|
+
"cooldown.total_count": this.cooldowns.size,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
logger.debug("[CooldownRegistry] Added cooldown entry", {
|
|
121
|
+
projectId: projectId.substring(0, 12),
|
|
122
|
+
conversationId: shortenConversationId(conversationId),
|
|
123
|
+
agentPubkey: agentPubkey.substring(0, 12),
|
|
124
|
+
reason,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Check if a project:conversation:agent tuple is in cooldown
|
|
130
|
+
*
|
|
131
|
+
* @param projectId - The project ID
|
|
132
|
+
* @param conversationId - The conversation ID
|
|
133
|
+
* @param agentPubkey - The agent's pubkey
|
|
134
|
+
* @returns true if the tuple is currently in cooldown, false otherwise
|
|
135
|
+
*/
|
|
136
|
+
isInCooldown(projectId: string, conversationId: string, agentPubkey: string): boolean {
|
|
137
|
+
const key = this.makeKey(projectId, conversationId, agentPubkey);
|
|
138
|
+
const entry = this.cooldowns.get(key);
|
|
139
|
+
|
|
140
|
+
if (!entry) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
const elapsed = now - entry.abortedAt;
|
|
146
|
+
|
|
147
|
+
// Check if cooldown has expired
|
|
148
|
+
if (elapsed > COOLDOWN_DURATION_MS) {
|
|
149
|
+
// Expired - remove and return false
|
|
150
|
+
this.cooldowns.delete(key);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Still in cooldown
|
|
155
|
+
trace.getActiveSpan()?.addEvent("cooldown.check_blocked", {
|
|
156
|
+
"cooldown.project_id": projectId.substring(0, 12),
|
|
157
|
+
"cooldown.conversation_id": shortenConversationId(conversationId),
|
|
158
|
+
"cooldown.agent_pubkey": agentPubkey.substring(0, 12),
|
|
159
|
+
"cooldown.elapsed_ms": elapsed,
|
|
160
|
+
"cooldown.remaining_ms": COOLDOWN_DURATION_MS - elapsed,
|
|
161
|
+
"cooldown.reason": entry.reason ?? "unknown",
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get all active cooldown entries (for debugging)
|
|
169
|
+
*/
|
|
170
|
+
getActiveCooldowns(): CooldownEntry[] {
|
|
171
|
+
const now = Date.now();
|
|
172
|
+
const active: CooldownEntry[] = [];
|
|
173
|
+
|
|
174
|
+
for (const entry of this.cooldowns.values()) {
|
|
175
|
+
if (now - entry.abortedAt <= COOLDOWN_DURATION_MS) {
|
|
176
|
+
active.push(entry);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return active;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Clear all cooldown entries (for testing)
|
|
185
|
+
*/
|
|
186
|
+
clearAll(): void {
|
|
187
|
+
this.cooldowns.clear();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Stop the cleanup interval (for testing/shutdown)
|
|
192
|
+
*/
|
|
193
|
+
destroy(): void {
|
|
194
|
+
if (this.cleanupInterval) {
|
|
195
|
+
clearInterval(this.cleanupInterval);
|
|
196
|
+
this.cleanupInterval = null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import type { ExecutionContext } from "@/agents/execution/types";
|
|
2
|
+
import type { MessageInjector } from "@/llm/types";
|
|
3
|
+
import { logger } from "@/utils/logger";
|
|
4
|
+
import { trace } from "@opentelemetry/api";
|
|
5
|
+
import { EventEmitter } from "tseep";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Constant used as the abort reason when aborting for mid-execution injection.
|
|
9
|
+
* Allows AgentExecutor to distinguish injection aborts from user-initiated stops.
|
|
10
|
+
*/
|
|
11
|
+
export const INJECTION_ABORT_REASON = "INJECTION_ABORT";
|
|
12
|
+
|
|
13
|
+
/** RAL state representing the current phase of agent execution */
|
|
14
|
+
export type RALState = "IDLE" | "REASONING" | "ACTING" | "STREAMING" | "ERROR";
|
|
15
|
+
|
|
16
|
+
// Store essential operation metadata
|
|
17
|
+
export interface LLMOperation {
|
|
18
|
+
id: string;
|
|
19
|
+
abortController: AbortController;
|
|
20
|
+
eventEmitter: EventEmitter; // For message injection into running executions
|
|
21
|
+
eventId: string; // The event being processed
|
|
22
|
+
agentPubkey: string; // Agent doing the work
|
|
23
|
+
conversationId: string; // Root event ID for conversation
|
|
24
|
+
registeredAt: number; // Timestamp
|
|
25
|
+
/** MessageInjector for Claude Code streams (set via onStreamStart callback) */
|
|
26
|
+
messageInjector?: MessageInjector;
|
|
27
|
+
/** Current RAL state for this operation */
|
|
28
|
+
ralState?: RALState;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class LLMOperationsRegistry {
|
|
32
|
+
private static instance: LLMOperationsRegistry;
|
|
33
|
+
private operations = new Map<string, LLMOperation>();
|
|
34
|
+
private byEvent = new Map<string, Set<string>>();
|
|
35
|
+
private operationsByContext = new Map<string, string>(); // contextKey -> operationId
|
|
36
|
+
private changeListeners = new Set<() => void>();
|
|
37
|
+
|
|
38
|
+
// DIAGNOSTIC: Concurrent streaming metrics
|
|
39
|
+
private peakConcurrentOperations = 0;
|
|
40
|
+
private totalOperationsRegistered = 0;
|
|
41
|
+
private concurrencyHistogram = new Map<number, number>(); // concurrency level -> count
|
|
42
|
+
|
|
43
|
+
static getInstance(): LLMOperationsRegistry {
|
|
44
|
+
if (!LLMOperationsRegistry.instance) {
|
|
45
|
+
LLMOperationsRegistry.instance = new LLMOperationsRegistry();
|
|
46
|
+
}
|
|
47
|
+
return LLMOperationsRegistry.instance;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
registerOperation(context: ExecutionContext): AbortSignal {
|
|
51
|
+
const operationId = crypto.randomUUID();
|
|
52
|
+
const conversation = context.getConversation();
|
|
53
|
+
const rootEventId = conversation?.getRootEventId() || context.triggeringEvent.id;
|
|
54
|
+
|
|
55
|
+
// Create operation with metadata
|
|
56
|
+
const operation: LLMOperation = {
|
|
57
|
+
id: operationId,
|
|
58
|
+
abortController: new AbortController(),
|
|
59
|
+
eventEmitter: new EventEmitter(),
|
|
60
|
+
eventId: context.triggeringEvent.id,
|
|
61
|
+
agentPubkey: context.agent.pubkey,
|
|
62
|
+
conversationId: rootEventId,
|
|
63
|
+
registeredAt: Date.now(),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Store the operation
|
|
67
|
+
this.operations.set(operationId, operation);
|
|
68
|
+
|
|
69
|
+
// Index by both root event and triggering event
|
|
70
|
+
this.indexOperation(operationId, rootEventId);
|
|
71
|
+
if (context.triggeringEvent.id !== rootEventId) {
|
|
72
|
+
this.indexOperation(operationId, context.triggeringEvent.id);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Also index by context for easy lookup on completion
|
|
76
|
+
this.operationsByContext.set(this.getContextKey(context), operationId);
|
|
77
|
+
|
|
78
|
+
// Auto-cleanup on abort (for cancellation cases)
|
|
79
|
+
operation.abortController.signal.addEventListener("abort", () => {
|
|
80
|
+
this.cleanupOperation(operationId);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
logger.debug("[LLMOpsRegistry] Registered operation", {
|
|
84
|
+
operationId: operationId.substring(0, 8),
|
|
85
|
+
rootEvent: rootEventId.substring(0, 8),
|
|
86
|
+
triggeringEvent: context.triggeringEvent.id.substring(0, 8),
|
|
87
|
+
agent: context.agent.name,
|
|
88
|
+
agentPubkey: context.agent.pubkey.substring(0, 8),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// DIAGNOSTIC: Track concurrent streaming metrics
|
|
92
|
+
this.totalOperationsRegistered++;
|
|
93
|
+
const currentConcurrency = this.operations.size;
|
|
94
|
+
if (currentConcurrency > this.peakConcurrentOperations) {
|
|
95
|
+
this.peakConcurrentOperations = currentConcurrency;
|
|
96
|
+
}
|
|
97
|
+
this.concurrencyHistogram.set(
|
|
98
|
+
currentConcurrency,
|
|
99
|
+
(this.concurrencyHistogram.get(currentConcurrency) || 0) + 1
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Emit OTL event for concurrent operation tracking
|
|
103
|
+
const activeSpan = trace.getActiveSpan();
|
|
104
|
+
activeSpan?.addEvent("llm_ops.operation_registered", {
|
|
105
|
+
"concurrent.current_count": currentConcurrency,
|
|
106
|
+
"concurrent.peak_count": this.peakConcurrentOperations,
|
|
107
|
+
"concurrent.total_registered": this.totalOperationsRegistered,
|
|
108
|
+
"operation.id": operationId.substring(0, 8),
|
|
109
|
+
"operation.agent_name": context.agent.name,
|
|
110
|
+
"operation.agent_pubkey": context.agent.pubkey.substring(0, 8),
|
|
111
|
+
"process.memory_heap_used_mb": Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
|
|
112
|
+
"process.memory_rss_mb": Math.round(process.memoryUsage().rss / 1024 / 1024),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Notify listeners of new operation
|
|
116
|
+
this.notifyChange();
|
|
117
|
+
|
|
118
|
+
return operation.abortController.signal;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
completeOperation(context: ExecutionContext): void {
|
|
122
|
+
const contextKey = this.getContextKey(context);
|
|
123
|
+
const operationId = this.operationsByContext.get(contextKey);
|
|
124
|
+
|
|
125
|
+
if (!operationId) {
|
|
126
|
+
// Operation was never registered or already completed
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Remove context mapping
|
|
131
|
+
this.operationsByContext.delete(contextKey);
|
|
132
|
+
|
|
133
|
+
// Do the actual cleanup
|
|
134
|
+
this.cleanupOperation(operationId);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private cleanupOperation(operationId: string): void {
|
|
138
|
+
const operation = this.operations.get(operationId);
|
|
139
|
+
if (!operation) {
|
|
140
|
+
// Already cleaned up
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Remove from main map
|
|
145
|
+
this.operations.delete(operationId);
|
|
146
|
+
|
|
147
|
+
// Remove from all indices
|
|
148
|
+
this.unindexOperation(operationId, operation.conversationId);
|
|
149
|
+
if (operation.eventId !== operation.conversationId) {
|
|
150
|
+
this.unindexOperation(operationId, operation.eventId);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const operationDuration = Date.now() - operation.registeredAt;
|
|
154
|
+
logger.debug("[LLMOpsRegistry] Completed operation", {
|
|
155
|
+
operationId: operationId.substring(0, 8),
|
|
156
|
+
eventId: operation.eventId.substring(0, 8),
|
|
157
|
+
conversationId: operation.conversationId.substring(0, 8),
|
|
158
|
+
duration: operationDuration,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// DIAGNOSTIC: Track operation completion with concurrency context
|
|
162
|
+
const remainingConcurrency = this.operations.size;
|
|
163
|
+
const activeSpan = trace.getActiveSpan();
|
|
164
|
+
activeSpan?.addEvent("llm_ops.operation_completed", {
|
|
165
|
+
"concurrent.remaining_count": remainingConcurrency,
|
|
166
|
+
"concurrent.peak_count": this.peakConcurrentOperations,
|
|
167
|
+
"operation.id": operationId.substring(0, 8),
|
|
168
|
+
"operation.duration_ms": operationDuration,
|
|
169
|
+
"operation.agent_pubkey": operation.agentPubkey.substring(0, 8),
|
|
170
|
+
"process.memory_heap_used_mb": Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Notify listeners of change
|
|
174
|
+
this.notifyChange();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private getContextKey(context: ExecutionContext): string {
|
|
178
|
+
// Create a unique key from the context that identifies this specific operation
|
|
179
|
+
return `${context.triggeringEvent.id}:${context.agent.pubkey}`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
stopByEventId(eventId: string): number {
|
|
183
|
+
const operationIds = this.byEvent.get(eventId) || new Set();
|
|
184
|
+
let stopped = 0;
|
|
185
|
+
|
|
186
|
+
for (const opId of operationIds) {
|
|
187
|
+
const operation = this.operations.get(opId);
|
|
188
|
+
if (operation && !operation.abortController.signal.aborted) {
|
|
189
|
+
operation.abortController.abort();
|
|
190
|
+
stopped++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (stopped > 0) {
|
|
195
|
+
logger.info("[LLMOpsRegistry] Stopped operations", {
|
|
196
|
+
eventId: eventId.substring(0, 8),
|
|
197
|
+
count: stopped,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return stopped;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Stop operations for a specific agent in a conversation.
|
|
206
|
+
* Used to abort streaming execution when mid-stream injection arrives.
|
|
207
|
+
*
|
|
208
|
+
* @param agentPubkey - The agent's public key
|
|
209
|
+
* @param conversationId - The conversation (root event) ID
|
|
210
|
+
* @param reason - Reason for the abort (passed to AbortController)
|
|
211
|
+
* @returns Whether an operation was aborted
|
|
212
|
+
*/
|
|
213
|
+
stopByAgentAndConversation(agentPubkey: string, conversationId: string, reason?: string): boolean {
|
|
214
|
+
for (const operation of this.operations.values()) {
|
|
215
|
+
if (
|
|
216
|
+
operation.agentPubkey === agentPubkey &&
|
|
217
|
+
operation.conversationId === conversationId &&
|
|
218
|
+
!operation.abortController.signal.aborted
|
|
219
|
+
) {
|
|
220
|
+
operation.abortController.abort(reason);
|
|
221
|
+
logger.info("[LLMOpsRegistry] Stopped operation for injection", {
|
|
222
|
+
operationId: operation.id.substring(0, 8),
|
|
223
|
+
agentPubkey: agentPubkey.substring(0, 8),
|
|
224
|
+
conversationId: conversationId.substring(0, 8),
|
|
225
|
+
reason,
|
|
226
|
+
});
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Set the message injector for a streaming Claude Code operation.
|
|
235
|
+
* Called from onStreamStart callback when the stream starts.
|
|
236
|
+
*
|
|
237
|
+
* @param agentPubkey - The agent's public key
|
|
238
|
+
* @param conversationId - The conversation (root event) ID
|
|
239
|
+
* @param injector - The MessageInjector instance
|
|
240
|
+
* @returns Whether an operation was found and updated
|
|
241
|
+
*/
|
|
242
|
+
setMessageInjector(agentPubkey: string, conversationId: string, injector: MessageInjector): boolean {
|
|
243
|
+
for (const operation of this.operations.values()) {
|
|
244
|
+
if (
|
|
245
|
+
operation.agentPubkey === agentPubkey &&
|
|
246
|
+
operation.conversationId === conversationId &&
|
|
247
|
+
!operation.abortController.signal.aborted
|
|
248
|
+
) {
|
|
249
|
+
operation.messageInjector = injector;
|
|
250
|
+
logger.debug("[LLMOpsRegistry] Set message injector", {
|
|
251
|
+
operationId: operation.id.substring(0, 8),
|
|
252
|
+
agentPubkey: agentPubkey.substring(0, 8),
|
|
253
|
+
conversationId: conversationId.substring(0, 8),
|
|
254
|
+
});
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get the message injector for a streaming Claude Code operation.
|
|
263
|
+
* Returns undefined if no active operation exists or no injector is set.
|
|
264
|
+
*
|
|
265
|
+
* @param agentPubkey - The agent's public key
|
|
266
|
+
* @param conversationId - The conversation (root event) ID
|
|
267
|
+
* @returns The MessageInjector or undefined
|
|
268
|
+
*/
|
|
269
|
+
getMessageInjector(agentPubkey: string, conversationId: string): MessageInjector | undefined {
|
|
270
|
+
for (const operation of this.operations.values()) {
|
|
271
|
+
if (
|
|
272
|
+
operation.agentPubkey === agentPubkey &&
|
|
273
|
+
operation.conversationId === conversationId &&
|
|
274
|
+
!operation.abortController.signal.aborted &&
|
|
275
|
+
operation.messageInjector
|
|
276
|
+
) {
|
|
277
|
+
return operation.messageInjector;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Update the RAL state for an operation identified by agent and conversation.
|
|
285
|
+
* This is called by RALRegistry when state transitions occur (streaming, tool execution, etc.)
|
|
286
|
+
*
|
|
287
|
+
* @param agentPubkey - The agent's public key
|
|
288
|
+
* @param conversationId - The conversation (root event) ID
|
|
289
|
+
* @param state - The new RAL state
|
|
290
|
+
* @returns Whether an operation was found and updated
|
|
291
|
+
*/
|
|
292
|
+
updateRALState(agentPubkey: string, conversationId: string, state: RALState): boolean {
|
|
293
|
+
for (const operation of this.operations.values()) {
|
|
294
|
+
if (
|
|
295
|
+
operation.agentPubkey === agentPubkey &&
|
|
296
|
+
operation.conversationId === conversationId &&
|
|
297
|
+
!operation.abortController.signal.aborted
|
|
298
|
+
) {
|
|
299
|
+
// Only update and notify if state actually changed
|
|
300
|
+
const previousState = operation.ralState;
|
|
301
|
+
if (previousState === state) {
|
|
302
|
+
return true; // Found but no change needed
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
operation.ralState = state;
|
|
306
|
+
logger.debug("[LLMOpsRegistry] Updated RAL state", {
|
|
307
|
+
operationId: operation.id.substring(0, 8),
|
|
308
|
+
agentPubkey: agentPubkey.substring(0, 8),
|
|
309
|
+
conversationId: conversationId.substring(0, 8),
|
|
310
|
+
previousState,
|
|
311
|
+
state,
|
|
312
|
+
});
|
|
313
|
+
// Notify listeners of state change
|
|
314
|
+
this.notifyChange();
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
getActiveOperationsCount(): number {
|
|
322
|
+
return this.operations.size;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* DIAGNOSTIC: Get concurrency statistics for bottleneck analysis
|
|
327
|
+
*/
|
|
328
|
+
getConcurrencyStats(): {
|
|
329
|
+
current: number;
|
|
330
|
+
peak: number;
|
|
331
|
+
total: number;
|
|
332
|
+
histogram: Record<number, number>;
|
|
333
|
+
activeAgents: string[];
|
|
334
|
+
} {
|
|
335
|
+
return {
|
|
336
|
+
current: this.operations.size,
|
|
337
|
+
peak: this.peakConcurrentOperations,
|
|
338
|
+
total: this.totalOperationsRegistered,
|
|
339
|
+
histogram: Object.fromEntries(this.concurrencyHistogram),
|
|
340
|
+
activeAgents: Array.from(this.operations.values()).map(
|
|
341
|
+
(op) => `${op.agentPubkey.substring(0, 8)}:${op.ralState || "unknown"}`
|
|
342
|
+
),
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* DIAGNOSTIC: Reset concurrency metrics (for testing)
|
|
348
|
+
*/
|
|
349
|
+
resetConcurrencyMetrics(): void {
|
|
350
|
+
this.peakConcurrentOperations = 0;
|
|
351
|
+
this.totalOperationsRegistered = 0;
|
|
352
|
+
this.concurrencyHistogram.clear();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Get operations grouped by conversation ID (thread root) for publishing.
|
|
357
|
+
* Operations are grouped by conversationId only, not by individual message eventId.
|
|
358
|
+
* This ensures kind:24133 status events use the thread root in the e-tag,
|
|
359
|
+
* allowing clients to look up working agents by conversation ID.
|
|
360
|
+
*/
|
|
361
|
+
getOperationsByConversation(): Map<string, LLMOperation[]> {
|
|
362
|
+
const byConversation = new Map<string, LLMOperation[]>();
|
|
363
|
+
|
|
364
|
+
for (const operation of this.operations.values()) {
|
|
365
|
+
this.addOperationToConversationMap(byConversation, operation.conversationId, operation);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return byConversation;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// NOTE: getOperationsByEvent() was removed - use getOperationsByConversation() instead.
|
|
372
|
+
// If you need per-event grouping, iterate operations and group manually.
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Helper to add an operation to the conversation map.
|
|
376
|
+
* Creates the array if it doesn't exist, then appends the operation.
|
|
377
|
+
*/
|
|
378
|
+
private addOperationToConversationMap(
|
|
379
|
+
map: Map<string, LLMOperation[]>,
|
|
380
|
+
conversationId: string,
|
|
381
|
+
operation: LLMOperation
|
|
382
|
+
): void {
|
|
383
|
+
let operations = map.get(conversationId);
|
|
384
|
+
if (!operations) {
|
|
385
|
+
operations = [];
|
|
386
|
+
map.set(conversationId, operations);
|
|
387
|
+
}
|
|
388
|
+
operations.push(operation);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Subscribe to changes
|
|
392
|
+
onChange(listener: () => void): () => void {
|
|
393
|
+
this.changeListeners.add(listener);
|
|
394
|
+
return () => this.changeListeners.delete(listener);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private notifyChange(): void {
|
|
398
|
+
for (const listener of this.changeListeners) {
|
|
399
|
+
listener();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Index an operation by event ID for fast lookup.
|
|
405
|
+
* Creates the Set if it doesn't exist, then adds the operation ID.
|
|
406
|
+
*/
|
|
407
|
+
private indexOperation(operationId: string, eventId: string): void {
|
|
408
|
+
let eventOperations = this.byEvent.get(eventId);
|
|
409
|
+
if (!eventOperations) {
|
|
410
|
+
eventOperations = new Set();
|
|
411
|
+
this.byEvent.set(eventId, eventOperations);
|
|
412
|
+
}
|
|
413
|
+
eventOperations.add(operationId);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
private unindexOperation(operationId: string, eventId: string): void {
|
|
417
|
+
this.byEvent.get(eventId)?.delete(operationId);
|
|
418
|
+
if (this.byEvent.get(eventId)?.size === 0) {
|
|
419
|
+
this.byEvent.delete(eventId);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export const llmOpsRegistry = LLMOperationsRegistry.getInstance();
|