@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,234 @@
|
|
|
1
|
+
import type { ConversationStore } from "@/conversations/ConversationStore";
|
|
2
|
+
import { CategoryManager } from "@/conversations/services";
|
|
3
|
+
import { NDKEventMetadata } from "@/events/NDKEventMetadata";
|
|
4
|
+
import { llmServiceFactory } from "@/llm";
|
|
5
|
+
import { shortenConversationId } from "@/utils/conversation-id";
|
|
6
|
+
import { NDKKind } from "@/nostr/kinds";
|
|
7
|
+
import { getNDK } from "@/nostr/ndkClient";
|
|
8
|
+
import { config } from "@/services/ConfigService";
|
|
9
|
+
import { getPubkeyService } from "@/services/PubkeyService";
|
|
10
|
+
import type { ProjectContext } from "@/services/projects";
|
|
11
|
+
import { ROOT_CONTEXT, SpanStatusCode, context as otelContext, trace } from "@opentelemetry/api";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
const tracer = trace.getTracer("tenex.summarizer");
|
|
15
|
+
|
|
16
|
+
export class ConversationSummarizer {
|
|
17
|
+
private categoryManager: CategoryManager;
|
|
18
|
+
|
|
19
|
+
constructor(private context: ProjectContext) {
|
|
20
|
+
// CategoryManager stores categories in ~/.tenex/data
|
|
21
|
+
this.categoryManager = new CategoryManager(config.getConfigPath());
|
|
22
|
+
this.categoryManager.initialize();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async summarizeAndPublish(conversation: ConversationStore): Promise<void> {
|
|
26
|
+
// Create a fresh span using ROOT_CONTEXT to avoid inheriting an ended span
|
|
27
|
+
// This is necessary because summarization runs debounced/async after the main processing span ends
|
|
28
|
+
const span = tracer.startSpan("tenex.summarize", {
|
|
29
|
+
attributes: {
|
|
30
|
+
"conversation.id": shortenConversationId(conversation.id),
|
|
31
|
+
},
|
|
32
|
+
}, ROOT_CONTEXT);
|
|
33
|
+
|
|
34
|
+
// Wrap execution in the new span's context so child operations use this span
|
|
35
|
+
return otelContext.with(trace.setSpan(ROOT_CONTEXT, span), async () => {
|
|
36
|
+
try {
|
|
37
|
+
// Get LLM configuration - use summarization config if set, otherwise default
|
|
38
|
+
const { llms } = await config.loadConfig();
|
|
39
|
+
const configName = llms.summarization || llms.default;
|
|
40
|
+
|
|
41
|
+
if (!configName) {
|
|
42
|
+
console.warn("No LLM configuration available for summarization");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Use getLLMConfig to resolve meta models automatically
|
|
47
|
+
const summarizationConfig = config.getLLMConfig(configName);
|
|
48
|
+
|
|
49
|
+
// Create LLM service
|
|
50
|
+
const llmService = llmServiceFactory.createService(
|
|
51
|
+
summarizationConfig,
|
|
52
|
+
{
|
|
53
|
+
agentName: "summarizer",
|
|
54
|
+
sessionId: `summarizer-${conversation.id}`,
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Prepare conversation content from stored messages
|
|
59
|
+
const messages = conversation.getAllMessages();
|
|
60
|
+
const pubkeyService = getPubkeyService();
|
|
61
|
+
|
|
62
|
+
// Resolve pubkeys to names for all participants
|
|
63
|
+
const textMessages = messages.filter((entry) => entry.messageType === "text");
|
|
64
|
+
const formattedMessages = await Promise.all(
|
|
65
|
+
textMessages.map(async (entry) => {
|
|
66
|
+
const name = await pubkeyService.getName(entry.pubkey);
|
|
67
|
+
return `${name}: ${entry.content}`;
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
const conversationContent = formattedMessages.join("\n\n");
|
|
71
|
+
|
|
72
|
+
if (!conversationContent.trim()) {
|
|
73
|
+
console.log("No content to summarize for conversation", conversation.id);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Get existing categories for consistency
|
|
78
|
+
const existingCategories = await this.categoryManager.getCategories();
|
|
79
|
+
const categoryListText = existingCategories.length > 0
|
|
80
|
+
? `Existing categories (prefer these for consistency): ${existingCategories.join(", ")}`
|
|
81
|
+
: "No existing categories yet. Create new ones as needed.";
|
|
82
|
+
|
|
83
|
+
// Generate title, summary, and status information
|
|
84
|
+
const { object: result } = await llmService.generateObject(
|
|
85
|
+
[
|
|
86
|
+
{
|
|
87
|
+
role: "system",
|
|
88
|
+
content: `You generate high-signal titles, summaries, status metadata, and category tags for technical conversations.
|
|
89
|
+
|
|
90
|
+
CRITICAL: Base output ONLY on what is explicitly stated in the conversation. Do NOT:
|
|
91
|
+
- Hallucinate success when errors, failures, or problems are mentioned
|
|
92
|
+
- Assume tasks were completed if the conversation shows they failed or are still in progress
|
|
93
|
+
- Invent outcomes not clearly stated in the transcript
|
|
94
|
+
|
|
95
|
+
DENSITY RULES (ENFORCE)
|
|
96
|
+
- Summary max 160 characters (hard limit).
|
|
97
|
+
- No narrative glue: avoid “ensuring”, “including”, “key features”, “focused on”, “review of”, “in order to”, “now complete”, “ready for testing” (unless explicitly stated).
|
|
98
|
+
- No redundancy: summary and status_current_activity must not restate the same fact in different words.
|
|
99
|
+
|
|
100
|
+
TITLE
|
|
101
|
+
- 3–5 words (hard limit), concrete nouns/verbs, no filler.
|
|
102
|
+
- Prefer outcome/topic phrasing.
|
|
103
|
+
|
|
104
|
+
SUMMARY (1 sentence only)
|
|
105
|
+
- Changelog style: state facts only (outcome/state, scope, blockers).
|
|
106
|
+
- For In Progress / Blocked / Waiting: include what is missing or unknown (“Details not provided”).
|
|
107
|
+
- Do not describe process.
|
|
108
|
+
|
|
109
|
+
STATUS
|
|
110
|
+
- status_label: one of "Researching", "In Progress", "Blocked", "Waiting", "Completed", "Failed", "Planning".
|
|
111
|
+
- status_current_activity: one dense clause, consistent with status_label.
|
|
112
|
+
- Do not duplicate the summary.
|
|
113
|
+
|
|
114
|
+
CATEGORIES (CANONICAL-FIRST, SEMANTIC)
|
|
115
|
+
You are given a list of previously used categories below. This list is a CANONICAL SUGGESTION SET, not an allowlist.
|
|
116
|
+
You must actively judge each candidate (including items from the list) using the rules below.
|
|
117
|
+
|
|
118
|
+
Previously used categories:
|
|
119
|
+
${categoryListText}
|
|
120
|
+
|
|
121
|
+
Selection rules:
|
|
122
|
+
- Prefer an existing category from the list *only if* it is a good semantic fit.
|
|
123
|
+
- A valid category must:
|
|
124
|
+
- Name a stable system concept (component, data model, protocol, UI artifact, subsystem)
|
|
125
|
+
- Remain meaningful months later without task context
|
|
126
|
+
- Have high discriminative value (would not apply to most unrelated conversations)
|
|
127
|
+
- Do NOT select a category just because it exists in the list.
|
|
128
|
+
|
|
129
|
+
Creation rules (to avoid fragmentation):
|
|
130
|
+
- Create a new category ONLY if no existing category fits well.
|
|
131
|
+
- If creating a new category:
|
|
132
|
+
- Use a simple, canonical noun form
|
|
133
|
+
- Avoid re-ordering words that would create near-duplicates
|
|
134
|
+
- Prefer the most general stable concept (e.g., “agent” over “agent-runtime” unless runtime is explicitly the core topic)
|
|
135
|
+
|
|
136
|
+
Rejection rule:
|
|
137
|
+
- If all plausible categories (including those from the list) are low-signal or process-oriented, output [].
|
|
138
|
+
|
|
139
|
+
Before emitting categories, silently verify for each:
|
|
140
|
+
- It maps to an explicit noun phrase in the transcript
|
|
141
|
+
- It passes the “6-months later” test
|
|
142
|
+
- It would not create a near-duplicate of an existing category`,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
role: "user",
|
|
146
|
+
content: `Please generate a title, summary, and status information for this conversation:\n\n${conversationContent}`,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
z.object({
|
|
150
|
+
title: z.string().describe("A concise title for the conversation (3-5 words)"),
|
|
151
|
+
summary: z
|
|
152
|
+
.string()
|
|
153
|
+
.describe(
|
|
154
|
+
"A 1-sentence, information-dense summary (<=160 chars) of key facts, scope, and blockers"
|
|
155
|
+
),
|
|
156
|
+
status_label: z
|
|
157
|
+
.string()
|
|
158
|
+
.describe(
|
|
159
|
+
"A concise status label (e.g., 'In Progress', 'Blocked', 'Waiting', 'Completed', 'Failed')"
|
|
160
|
+
),
|
|
161
|
+
status_current_activity: z
|
|
162
|
+
.string()
|
|
163
|
+
.describe(
|
|
164
|
+
"One dense clause consistent with status_label; no duplication or speculation"
|
|
165
|
+
),
|
|
166
|
+
categories: z
|
|
167
|
+
.array(z.string())
|
|
168
|
+
.max(3)
|
|
169
|
+
.describe(
|
|
170
|
+
"0-3 category tags. Lowercase singular nouns. Prefer canonical list; create new only if necessary; may be empty []."
|
|
171
|
+
),
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
// Publish metadata event
|
|
178
|
+
const ndk = getNDK();
|
|
179
|
+
const event = new NDKEventMetadata(ndk);
|
|
180
|
+
event.kind = NDKKind.EventMetadata;
|
|
181
|
+
event.setConversationId(conversation.id);
|
|
182
|
+
|
|
183
|
+
// Add metadata tags
|
|
184
|
+
if (result.title) {
|
|
185
|
+
event.tags.push(["title", result.title]);
|
|
186
|
+
}
|
|
187
|
+
if (result.summary) {
|
|
188
|
+
event.tags.push(["summary", result.summary]);
|
|
189
|
+
}
|
|
190
|
+
if (result.status_label) {
|
|
191
|
+
event.tags.push(["status-label", result.status_label]);
|
|
192
|
+
}
|
|
193
|
+
if (result.status_current_activity) {
|
|
194
|
+
event.tags.push(["status-current-activity", result.status_current_activity]);
|
|
195
|
+
}
|
|
196
|
+
if (result.categories && result.categories.length > 0) {
|
|
197
|
+
for (const category of result.categories) {
|
|
198
|
+
event.tags.push(["t", category]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
event.tags.push(["a", this.context.project.tagId()]); // Project reference
|
|
202
|
+
event.tags.push(["model", summarizationConfig.model]);
|
|
203
|
+
|
|
204
|
+
// Sign and publish with backend signer
|
|
205
|
+
const backendSigner = await config.getBackendSigner();
|
|
206
|
+
await event.sign(backendSigner);
|
|
207
|
+
event.publish();
|
|
208
|
+
console.log(
|
|
209
|
+
`Published metadata for conversation ${conversation.id}: ${result.title}`
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Also persist summary to local metadata for prompt fragments
|
|
213
|
+
// This ensures "Recent Conversations" section can display summaries
|
|
214
|
+
conversation.updateMetadata({
|
|
215
|
+
summary: result.summary,
|
|
216
|
+
});
|
|
217
|
+
await conversation.save();
|
|
218
|
+
|
|
219
|
+
// Update category tally for future consistency
|
|
220
|
+
if (result.categories && result.categories.length > 0) {
|
|
221
|
+
await this.categoryManager.updateCategories(result.categories);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
225
|
+
} catch (error) {
|
|
226
|
+
span.recordException(error as Error);
|
|
227
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
228
|
+
console.error("Error generating conversation summary:", error);
|
|
229
|
+
} finally {
|
|
230
|
+
span.end();
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { logger } from "@/utils/logger";
|
|
2
|
+
|
|
3
|
+
interface DebounceState {
|
|
4
|
+
timerId: NodeJS.Timeout | null;
|
|
5
|
+
firstExecutionDone: boolean;
|
|
6
|
+
maxDeadline: number; // Unix timestamp (ms)
|
|
7
|
+
pendingPublishFn: (() => Promise<void>) | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const DEBOUNCE_MS = 10_000; // 10 seconds
|
|
11
|
+
const MAX_DELAY_MS = 5 * 60_000; // 5 minutes
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Manages debounced publishing of kind 513 metadata events at conversation level.
|
|
15
|
+
*
|
|
16
|
+
* Strategy:
|
|
17
|
+
* - First execution (or root event): publish immediately
|
|
18
|
+
* - Subsequent executions: debounce by 10 seconds
|
|
19
|
+
* - If another agent starts on same conversation: reset the debounce timer
|
|
20
|
+
* - Max delay of 5 minutes before forcing publication
|
|
21
|
+
*/
|
|
22
|
+
class MetadataDebounceManager {
|
|
23
|
+
private debounceStates = new Map<string, DebounceState>();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Called when an agent STARTS execution on a conversation.
|
|
27
|
+
* Resets any pending debounce timer (but preserves the publish function).
|
|
28
|
+
*/
|
|
29
|
+
onAgentStart(conversationId: string): void {
|
|
30
|
+
const state = this.debounceStates.get(conversationId);
|
|
31
|
+
if (state?.timerId) {
|
|
32
|
+
clearTimeout(state.timerId);
|
|
33
|
+
state.timerId = null;
|
|
34
|
+
logger.debug("[MetadataDebounce] Timer reset on agent start", {
|
|
35
|
+
conversationId: conversationId.substring(0, 8),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Called when an agent COMPLETES execution.
|
|
42
|
+
* Either publishes immediately (first exec or root event) or schedules debounced publish.
|
|
43
|
+
*/
|
|
44
|
+
schedulePublish(
|
|
45
|
+
conversationId: string,
|
|
46
|
+
isRootEvent: boolean,
|
|
47
|
+
publishFn: () => Promise<void>
|
|
48
|
+
): void {
|
|
49
|
+
let state = this.debounceStates.get(conversationId);
|
|
50
|
+
|
|
51
|
+
// First execution or root event: publish immediately
|
|
52
|
+
if (!state || !state.firstExecutionDone || isRootEvent) {
|
|
53
|
+
logger.debug("[MetadataDebounce] Publishing immediately (first/root)", {
|
|
54
|
+
conversationId: conversationId.substring(0, 8),
|
|
55
|
+
isRootEvent,
|
|
56
|
+
isFirstExecution: !state?.firstExecutionDone,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Initialize state
|
|
60
|
+
if (!state) {
|
|
61
|
+
state = {
|
|
62
|
+
timerId: null,
|
|
63
|
+
firstExecutionDone: true,
|
|
64
|
+
maxDeadline: Date.now() + MAX_DELAY_MS,
|
|
65
|
+
pendingPublishFn: null,
|
|
66
|
+
};
|
|
67
|
+
this.debounceStates.set(conversationId, state);
|
|
68
|
+
} else {
|
|
69
|
+
state.firstExecutionDone = true;
|
|
70
|
+
state.maxDeadline = Date.now() + MAX_DELAY_MS;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Publish immediately (fire and forget)
|
|
74
|
+
publishFn().catch((error) => {
|
|
75
|
+
logger.error("[MetadataDebounce] Failed to publish metadata", {
|
|
76
|
+
conversationId: conversationId.substring(0, 8),
|
|
77
|
+
error,
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Subsequent execution: debounce
|
|
84
|
+
// Clear existing timer if any
|
|
85
|
+
if (state.timerId) {
|
|
86
|
+
clearTimeout(state.timerId);
|
|
87
|
+
state.timerId = null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Update the pending publish function (use latest)
|
|
91
|
+
state.pendingPublishFn = publishFn;
|
|
92
|
+
|
|
93
|
+
const now = Date.now();
|
|
94
|
+
|
|
95
|
+
// Check if we've exceeded max deadline
|
|
96
|
+
if (now >= state.maxDeadline) {
|
|
97
|
+
logger.debug("[MetadataDebounce] Max deadline reached, publishing now", {
|
|
98
|
+
conversationId: conversationId.substring(0, 8),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Reset deadline for next batch
|
|
102
|
+
state.maxDeadline = now + MAX_DELAY_MS;
|
|
103
|
+
|
|
104
|
+
publishFn().catch((error) => {
|
|
105
|
+
logger.error("[MetadataDebounce] Failed to publish metadata", {
|
|
106
|
+
conversationId: conversationId.substring(0, 8),
|
|
107
|
+
error,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Schedule debounced publish
|
|
114
|
+
const delay = Math.min(DEBOUNCE_MS, state.maxDeadline - now);
|
|
115
|
+
|
|
116
|
+
logger.debug("[MetadataDebounce] Scheduling debounced publish", {
|
|
117
|
+
conversationId: conversationId.substring(0, 8),
|
|
118
|
+
delayMs: delay,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
state.timerId = setTimeout(() => {
|
|
122
|
+
const currentState = this.debounceStates.get(conversationId);
|
|
123
|
+
if (currentState?.pendingPublishFn) {
|
|
124
|
+
// Reset deadline for next batch
|
|
125
|
+
currentState.maxDeadline = Date.now() + MAX_DELAY_MS;
|
|
126
|
+
currentState.timerId = null;
|
|
127
|
+
|
|
128
|
+
currentState.pendingPublishFn().catch((error) => {
|
|
129
|
+
logger.error("[MetadataDebounce] Failed to publish metadata (debounced)", {
|
|
130
|
+
conversationId: conversationId.substring(0, 8),
|
|
131
|
+
error,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
currentState.pendingPublishFn = null;
|
|
135
|
+
}
|
|
136
|
+
}, delay);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Mark the first publish as done for a conversation.
|
|
141
|
+
* This ensures subsequent schedulePublish calls will debounce instead of publishing immediately.
|
|
142
|
+
* Use this when doing immediate metadata generation outside the debounce manager.
|
|
143
|
+
*/
|
|
144
|
+
markFirstPublishDone(conversationId: string): void {
|
|
145
|
+
let state = this.debounceStates.get(conversationId);
|
|
146
|
+
if (!state) {
|
|
147
|
+
state = {
|
|
148
|
+
timerId: null,
|
|
149
|
+
firstExecutionDone: true,
|
|
150
|
+
maxDeadline: Date.now() + MAX_DELAY_MS,
|
|
151
|
+
pendingPublishFn: null,
|
|
152
|
+
};
|
|
153
|
+
this.debounceStates.set(conversationId, state);
|
|
154
|
+
} else {
|
|
155
|
+
state.firstExecutionDone = true;
|
|
156
|
+
}
|
|
157
|
+
logger.debug("[MetadataDebounce] Marked first publish done", {
|
|
158
|
+
conversationId: conversationId.substring(0, 8),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Cleanup for a specific conversation. Clears timer without publishing.
|
|
164
|
+
*/
|
|
165
|
+
cleanup(conversationId: string): void {
|
|
166
|
+
const state = this.debounceStates.get(conversationId);
|
|
167
|
+
if (state?.timerId) {
|
|
168
|
+
clearTimeout(state.timerId);
|
|
169
|
+
}
|
|
170
|
+
this.debounceStates.delete(conversationId);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Cleanup all state. Called on shutdown.
|
|
175
|
+
*/
|
|
176
|
+
cleanupAll(): void {
|
|
177
|
+
for (const state of this.debounceStates.values()) {
|
|
178
|
+
if (state.timerId) {
|
|
179
|
+
clearTimeout(state.timerId);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
this.debounceStates.clear();
|
|
183
|
+
logger.debug("[MetadataDebounce] All timers cleared on shutdown");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Singleton instance
|
|
188
|
+
export const metadataDebounceManager = new MetadataDebounceManager();
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { ToolCallPart, ToolResultPart } from "ai";
|
|
2
|
+
import type { TodoItem } from "@/services/ral/types";
|
|
3
|
+
|
|
4
|
+
export type MessageType = "text" | "tool-call" | "tool-result" | "delegation-marker";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Marker stored in conversation history to track delegation lifecycle.
|
|
8
|
+
* Markers are created immediately when a delegation is initiated (status: "pending"),
|
|
9
|
+
* and updated when the delegation completes or is aborted.
|
|
10
|
+
* Instead of embedding the full transcript inline, we store a reference
|
|
11
|
+
* and lazily expand it when building messages.
|
|
12
|
+
*/
|
|
13
|
+
export interface DelegationMarker {
|
|
14
|
+
/** The delegation conversation ID (used to retrieve transcript) */
|
|
15
|
+
delegationConversationId: string;
|
|
16
|
+
/** The agent pubkey that received the delegation */
|
|
17
|
+
recipientPubkey: string;
|
|
18
|
+
/** The conversation ID of the parent (delegator) - for direct-child validation */
|
|
19
|
+
parentConversationId: string;
|
|
20
|
+
/** When the delegation was initiated (Unix timestamp in seconds) */
|
|
21
|
+
initiatedAt?: number;
|
|
22
|
+
/** When the delegation completed (Unix timestamp in seconds) - only set when completed/aborted */
|
|
23
|
+
completedAt?: number;
|
|
24
|
+
/** Delegation status: pending (in progress), completed (successful), or aborted */
|
|
25
|
+
status: "pending" | "completed" | "aborted";
|
|
26
|
+
/** If aborted, the reason for the abort */
|
|
27
|
+
abortReason?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ConversationEntry {
|
|
31
|
+
pubkey: string;
|
|
32
|
+
ral?: number; // Only for agent messages
|
|
33
|
+
content: string; // Text content (for text messages) or empty for tool messages
|
|
34
|
+
messageType: MessageType;
|
|
35
|
+
toolData?: ToolCallPart[] | ToolResultPart[]; // Only for tool-call and tool-result
|
|
36
|
+
eventId?: string; // If published to Nostr
|
|
37
|
+
timestamp?: number; // Unix timestamp (seconds) - from NDKEvent.created_at or Date.now()/1000
|
|
38
|
+
targetedPubkeys?: string[]; // Agent pubkeys this message is directed to (from p-tags)
|
|
39
|
+
/** Original sender pubkey for injected messages (for attribution when sender differs from expected) */
|
|
40
|
+
senderPubkey?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Explicit role override for synthetic entries (e.g., compressed summaries).
|
|
43
|
+
* When present, this role is used instead of deriving from pubkey.
|
|
44
|
+
* Used to ensure compressed summaries are rendered as "system" role, not "user".
|
|
45
|
+
*/
|
|
46
|
+
role?: "user" | "assistant" | "tool" | "system";
|
|
47
|
+
/**
|
|
48
|
+
* For delegation-marker messageType: contains the marker data.
|
|
49
|
+
* This allows lazy expansion of delegation transcripts when building messages.
|
|
50
|
+
*/
|
|
51
|
+
delegationMarker?: DelegationMarker;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface Injection {
|
|
55
|
+
targetRal: { pubkey: string; ral: number };
|
|
56
|
+
role: "user" | "system";
|
|
57
|
+
content: string;
|
|
58
|
+
queuedAt: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Deferred injection - a message to be injected on the agent's NEXT turn.
|
|
63
|
+
*
|
|
64
|
+
* Unlike Injection which targets a specific RAL, DeferredInjection is consumed
|
|
65
|
+
* at the START of any future RAL for the target agent. This is used for
|
|
66
|
+
* supervision messages that should NOT block the current completion but should
|
|
67
|
+
* appear in the agent's next conversation turn.
|
|
68
|
+
*/
|
|
69
|
+
export interface DeferredInjection {
|
|
70
|
+
/** The agent pubkey this injection is for */
|
|
71
|
+
targetPubkey: string;
|
|
72
|
+
/** The role of the injected message */
|
|
73
|
+
role: "system";
|
|
74
|
+
/** The message content */
|
|
75
|
+
content: string;
|
|
76
|
+
/** When this injection was queued (ms since epoch) */
|
|
77
|
+
queuedAt: number;
|
|
78
|
+
/** Optional source identifier for debugging (e.g., "supervision:consecutive-tools-without-todo") */
|
|
79
|
+
source?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Represents a participant in the delegation chain.
|
|
84
|
+
* Can be either a human user or an agent.
|
|
85
|
+
*/
|
|
86
|
+
export interface DelegationChainEntry {
|
|
87
|
+
/** The pubkey of the participant */
|
|
88
|
+
pubkey: string;
|
|
89
|
+
/** The display name (agent slug or shortened pubkey) */
|
|
90
|
+
displayName: string;
|
|
91
|
+
/** Whether this is the project owner/human user */
|
|
92
|
+
isUser: boolean;
|
|
93
|
+
/** The conversation ID where this delegation occurred (full ID, truncated only at display time) */
|
|
94
|
+
conversationId?: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface ConversationMetadata {
|
|
98
|
+
title?: string;
|
|
99
|
+
branch?: string;
|
|
100
|
+
summary?: string;
|
|
101
|
+
requirements?: string;
|
|
102
|
+
plan?: string;
|
|
103
|
+
projectPath?: string;
|
|
104
|
+
last_user_message?: string;
|
|
105
|
+
statusLabel?: string;
|
|
106
|
+
statusCurrentActivity?: string;
|
|
107
|
+
referencedArticle?: {
|
|
108
|
+
title: string;
|
|
109
|
+
content: string;
|
|
110
|
+
dTag: string;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* The delegation chain showing who initiated this conversation.
|
|
114
|
+
* First entry is the original initiator (typically User), last entry is the current agent.
|
|
115
|
+
* Example: [User, pm-wip, execution-coordinator, claude-code]
|
|
116
|
+
*/
|
|
117
|
+
delegationChain?: DelegationChainEntry[];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface RalTracker {
|
|
121
|
+
id: number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface ExecutionTime {
|
|
125
|
+
totalSeconds: number;
|
|
126
|
+
currentSessionStart?: number;
|
|
127
|
+
isActive: boolean;
|
|
128
|
+
lastUpdated: number;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface ConversationState {
|
|
132
|
+
activeRal: Record<string, RalTracker[]>;
|
|
133
|
+
nextRalNumber: Record<string, number>;
|
|
134
|
+
injections: Injection[];
|
|
135
|
+
messages: ConversationEntry[];
|
|
136
|
+
metadata: ConversationMetadata;
|
|
137
|
+
agentTodos: Record<string, TodoItem[]>;
|
|
138
|
+
todoNudgedAgents: string[]; // Agents who have been nudged about todo usage
|
|
139
|
+
blockedAgents: string[];
|
|
140
|
+
executionTime: ExecutionTime;
|
|
141
|
+
/** Meta model variant override per agent - when set, uses this variant instead of keyword detection */
|
|
142
|
+
metaModelVariantOverride?: Record<string, string>; // agentPubkey -> variantName
|
|
143
|
+
/**
|
|
144
|
+
* Deferred injections - messages to be injected on an agent's NEXT turn.
|
|
145
|
+
* Used by supervision for non-blocking nudges that shouldn't prevent completion.
|
|
146
|
+
*/
|
|
147
|
+
deferredInjections?: DeferredInjection[];
|
|
148
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content utilities for processing conversation messages
|
|
3
|
+
* Purpose: Strip <thinking>...</thinking> blocks from conversation history; skip messages that are purely thinking blocks.
|
|
4
|
+
* Also filter out events with reasoning tags.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Regex pattern to match thinking blocks (case-insensitive, multi-line)
|
|
11
|
+
* Matches: <thinking>, <Thinking>, <THINKING> with any attributes and their closing tags
|
|
12
|
+
*/
|
|
13
|
+
const THINKING_BLOCK_REGEX = /<thinking\b[^>]*>[\s\S]*?<\/thinking>/gi;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Remove all thinking blocks from content
|
|
17
|
+
* @param content - The content to process
|
|
18
|
+
* @returns The content with all thinking blocks removed and normalized whitespace (multiple blank lines collapsed to single newline)
|
|
19
|
+
*/
|
|
20
|
+
export function stripThinkingBlocks(content: string): string {
|
|
21
|
+
if (!content) return "";
|
|
22
|
+
|
|
23
|
+
// Remove all thinking blocks
|
|
24
|
+
let stripped = content.replace(THINKING_BLOCK_REGEX, "");
|
|
25
|
+
|
|
26
|
+
// Normalize whitespace more carefully:
|
|
27
|
+
// 1. Only collapse multiple spaces that aren't at the beginning of a line (preserve indentation)
|
|
28
|
+
// 2. Collapse multiple blank lines to a single newline
|
|
29
|
+
stripped = stripped
|
|
30
|
+
.split("\n")
|
|
31
|
+
.map((line) => {
|
|
32
|
+
// Only collapse spaces in the middle of lines, not at the start (preserve indentation)
|
|
33
|
+
if (line.trimStart() !== line) {
|
|
34
|
+
// Line has leading whitespace - preserve it
|
|
35
|
+
const leadingWhitespace = line.match(/^\s*/)?.[0] || "";
|
|
36
|
+
const rest = line.slice(leadingWhitespace.length);
|
|
37
|
+
return leadingWhitespace + rest.replace(/ {2,}/g, " ");
|
|
38
|
+
}
|
|
39
|
+
// No leading whitespace - collapse all multiple spaces
|
|
40
|
+
return line.replace(/ {2,}/g, " ");
|
|
41
|
+
})
|
|
42
|
+
.join("\n")
|
|
43
|
+
.replace(/\n\s*\n+/g, "\n") // Collapse 2+ newlines to single newline
|
|
44
|
+
.trim(); // Trim leading/trailing whitespace
|
|
45
|
+
|
|
46
|
+
return stripped;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if content contains only thinking blocks (no other content)
|
|
51
|
+
* @param content - The content to check
|
|
52
|
+
* @returns True if the content is empty after removing thinking blocks
|
|
53
|
+
*/
|
|
54
|
+
export function isOnlyThinkingBlocks(content: string): boolean {
|
|
55
|
+
if (!content || content.trim().length === 0) return false; // Empty/whitespace content is not "only thinking blocks"
|
|
56
|
+
|
|
57
|
+
const stripped = stripThinkingBlocks(content);
|
|
58
|
+
return stripped.length === 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if an event has a reasoning tag
|
|
63
|
+
* @param event - The NDK event to check
|
|
64
|
+
* @returns True if the event has a ["reasoning"] tag
|
|
65
|
+
*/
|
|
66
|
+
export function hasReasoningTag(event: NDKEvent): boolean {
|
|
67
|
+
if (!event.tags) return false;
|
|
68
|
+
return event.tags.some((tag) => tag[0] === "reasoning" && tag.length === 1);
|
|
69
|
+
}
|