@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,661 @@
|
|
|
1
|
+
import type { ToolExecutionContext } from "@/tools/types";
|
|
2
|
+
import { ConversationStore } from "@/conversations/ConversationStore";
|
|
3
|
+
import { llmServiceFactory } from "@/llm";
|
|
4
|
+
import { config } from "@/services/ConfigService";
|
|
5
|
+
import { getPubkeyService } from "@/services/PubkeyService";
|
|
6
|
+
import type { AISdkTool } from "@/tools/types";
|
|
7
|
+
import { logger } from "@/utils/logger";
|
|
8
|
+
import { isHexPrefix, resolvePrefixToId, PREFIX_LENGTH } from "@/utils/nostr-entity-parser";
|
|
9
|
+
import { tool } from "ai";
|
|
10
|
+
import type { ToolCallPart, ToolResultPart } from "ai";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { nip19 } from "nostr-tools";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Normalizes various event ID formats to a canonical 64-char lowercase hex ID.
|
|
16
|
+
*
|
|
17
|
+
* Accepts:
|
|
18
|
+
* - Full 64-character hex IDs
|
|
19
|
+
* - 12-character hex prefixes (resolved via PrefixKVStore)
|
|
20
|
+
* - NIP-19 formats: note1..., nevent1...
|
|
21
|
+
* - nostr: prefixed versions of all the above
|
|
22
|
+
*
|
|
23
|
+
* @param input - The event ID in any supported format
|
|
24
|
+
* @returns The normalized 64-char hex ID, or null if resolution fails
|
|
25
|
+
*/
|
|
26
|
+
function normalizeEventId(input: string): string | null {
|
|
27
|
+
const trimmed = input.trim();
|
|
28
|
+
|
|
29
|
+
// Strip nostr: prefix if present
|
|
30
|
+
const cleaned = trimmed.startsWith("nostr:") ? trimmed.slice(6) : trimmed;
|
|
31
|
+
|
|
32
|
+
// 1. Check for full 64-char hex ID
|
|
33
|
+
if (/^[0-9a-f]{64}$/i.test(cleaned)) {
|
|
34
|
+
return cleaned.toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 2. Check for 12-char hex prefix - resolve via PrefixKVStore
|
|
38
|
+
if (isHexPrefix(cleaned)) {
|
|
39
|
+
const resolved = resolvePrefixToId(cleaned);
|
|
40
|
+
return resolved; // Returns null if not found
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 3. Try NIP-19 decoding (note1..., nevent1...)
|
|
44
|
+
try {
|
|
45
|
+
const decoded = nip19.decode(cleaned);
|
|
46
|
+
if (decoded.type === "note") {
|
|
47
|
+
return (decoded.data as string).toLowerCase();
|
|
48
|
+
}
|
|
49
|
+
if (decoded.type === "nevent") {
|
|
50
|
+
return (decoded.data as { id: string }).id.toLowerCase();
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
// Not a valid NIP-19 format, fall through
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Invalid format or resolution failed
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const conversationGetSchema = z.object({
|
|
61
|
+
conversationId: z
|
|
62
|
+
.string()
|
|
63
|
+
.min(1, "conversationId is required")
|
|
64
|
+
.describe("The conversation ID to retrieve. Accepts full 64-char hex IDs (case-insensitive), 12-character hex prefixes, NIP-19 formats (note1..., nevent1...), or nostr: prefixed versions of any format."),
|
|
65
|
+
untilId: z
|
|
66
|
+
.string()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe(
|
|
69
|
+
"Optional message ID to retrieve conversation slice up to and including this message. Accepts full 64-char hex IDs (case-insensitive), 12-character hex prefixes, NIP-19 formats (note1..., nevent1...), or nostr: prefixed versions of any format. Useful for synthetic conversation forks where a new conversation references a parent conversation up to a specific message point."
|
|
70
|
+
),
|
|
71
|
+
prompt: z
|
|
72
|
+
.string()
|
|
73
|
+
.optional()
|
|
74
|
+
.describe(
|
|
75
|
+
"Optional prompt to analyze the conversation. When provided, the conversation will be processed through an LLM which will provide an explanation based on this prompt. Useful for extracting specific information or getting a summary of the conversation."
|
|
76
|
+
),
|
|
77
|
+
includeToolResults: z
|
|
78
|
+
.boolean()
|
|
79
|
+
.optional()
|
|
80
|
+
.default(false)
|
|
81
|
+
.describe(
|
|
82
|
+
"Whether to include tool result content in the response. WARNING: This can significantly increase token usage (up to 50k tokens). Tool results are truncated at 10k chars each with a 50k total budget. Only enable if you specifically need to analyze tool outputs."
|
|
83
|
+
),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
type ConversationGetInput = z.infer<typeof conversationGetSchema>;
|
|
87
|
+
|
|
88
|
+
interface ConversationGetOutput {
|
|
89
|
+
success: boolean;
|
|
90
|
+
conversation?: Record<string, unknown>;
|
|
91
|
+
explanation?: string;
|
|
92
|
+
message?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Recursively deep copy an object while handling cycles, BigInts, Maps, Sets, and other edge cases
|
|
97
|
+
*/
|
|
98
|
+
function safeDeepCopy(obj: unknown, seen = new WeakSet()): unknown {
|
|
99
|
+
// Handle primitives and special values
|
|
100
|
+
if (obj === null || typeof obj !== "object") {
|
|
101
|
+
if (typeof obj === "bigint") return obj.toString();
|
|
102
|
+
if (typeof obj === "function") return undefined;
|
|
103
|
+
return obj;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Cycle detection
|
|
107
|
+
if (seen.has(obj)) {
|
|
108
|
+
return "[Circular]";
|
|
109
|
+
}
|
|
110
|
+
seen.add(obj);
|
|
111
|
+
|
|
112
|
+
// Handle Arrays
|
|
113
|
+
if (Array.isArray(obj)) {
|
|
114
|
+
return obj.map(item => safeDeepCopy(item, seen));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Handle Maps
|
|
118
|
+
if (obj instanceof Map) {
|
|
119
|
+
const result: Record<string, unknown> = {};
|
|
120
|
+
for (const [key, value] of obj) {
|
|
121
|
+
result[String(key)] = safeDeepCopy(value, seen);
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Handle Sets
|
|
127
|
+
if (obj instanceof Set) {
|
|
128
|
+
return Array.from(obj).map(item => safeDeepCopy(item, seen));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Handle Dates
|
|
132
|
+
if (obj instanceof Date) {
|
|
133
|
+
return obj.toISOString();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Handle plain Objects
|
|
137
|
+
const result: Record<string, unknown> = {};
|
|
138
|
+
for (const key in obj) {
|
|
139
|
+
// Only process own properties
|
|
140
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
141
|
+
try {
|
|
142
|
+
result[key] = safeDeepCopy((obj as Record<string, unknown>)[key], seen);
|
|
143
|
+
} catch {
|
|
144
|
+
result[key] = "[Access Error]";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Safely copy data while handling circular references, BigInts, Maps, Sets, and other edge cases
|
|
153
|
+
* Uses recursive deep copy with cycle detection instead of JSON.stringify
|
|
154
|
+
*/
|
|
155
|
+
function safeCopy<T>(data: T): T {
|
|
156
|
+
try {
|
|
157
|
+
return safeDeepCopy(data) as T;
|
|
158
|
+
} catch {
|
|
159
|
+
// Fallback to string representation if even deep copy fails
|
|
160
|
+
return "[Serialization Failed]" as unknown as T;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Safely stringify a value, handling BigInt, circular refs, and other edge cases
|
|
166
|
+
* Returns a JSON string representation or "[Unserializable]" on failure
|
|
167
|
+
*/
|
|
168
|
+
function safeStringify(value: unknown): string {
|
|
169
|
+
if (value === undefined) return "";
|
|
170
|
+
if (value === null) return "null";
|
|
171
|
+
try {
|
|
172
|
+
return JSON.stringify(value);
|
|
173
|
+
} catch {
|
|
174
|
+
// Handle BigInt, circular references, or other unserializable values
|
|
175
|
+
return '"[Unserializable]"';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const MAX_PARAM_LENGTH = 100;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Format tool input parameters with per-param truncation
|
|
183
|
+
* Each param value is truncated at MAX_PARAM_LENGTH chars
|
|
184
|
+
* Format: param1="value1" param2="value2..." (N chars truncated)
|
|
185
|
+
*/
|
|
186
|
+
function formatToolInput(input: unknown): string {
|
|
187
|
+
if (input === undefined || input === null) return "";
|
|
188
|
+
|
|
189
|
+
// If input is not an object, just stringify and truncate the whole thing
|
|
190
|
+
if (typeof input !== "object" || Array.isArray(input)) {
|
|
191
|
+
const str = safeStringify(input);
|
|
192
|
+
if (str.length > MAX_PARAM_LENGTH) {
|
|
193
|
+
const truncated = str.length - MAX_PARAM_LENGTH;
|
|
194
|
+
return `${str.slice(0, MAX_PARAM_LENGTH)}... (${truncated} chars truncated)`;
|
|
195
|
+
}
|
|
196
|
+
return str;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// For objects, format each param with truncation
|
|
200
|
+
const parts: string[] = [];
|
|
201
|
+
const obj = input as Record<string, unknown>;
|
|
202
|
+
|
|
203
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
204
|
+
let valueStr: string;
|
|
205
|
+
try {
|
|
206
|
+
valueStr = JSON.stringify(value);
|
|
207
|
+
} catch {
|
|
208
|
+
valueStr = '"[Unserializable]"';
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (valueStr.length > MAX_PARAM_LENGTH) {
|
|
212
|
+
const truncated = valueStr.length - MAX_PARAM_LENGTH;
|
|
213
|
+
parts.push(`${key}=${valueStr.slice(0, MAX_PARAM_LENGTH)}... (${truncated} chars truncated)`);
|
|
214
|
+
} else {
|
|
215
|
+
parts.push(`${key}=${valueStr}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return parts.join(" ");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const MAX_LINE_LENGTH = 1500;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Format a single line with timestamp, sender, target(s), and content
|
|
226
|
+
* Truncates the full line INCLUDING suffix to maxLength chars
|
|
227
|
+
*/
|
|
228
|
+
function formatLine(
|
|
229
|
+
relativeSeconds: number,
|
|
230
|
+
from: string,
|
|
231
|
+
targets: string[] | undefined,
|
|
232
|
+
content: string,
|
|
233
|
+
maxLength: number = MAX_LINE_LENGTH
|
|
234
|
+
): string {
|
|
235
|
+
// Build target string: no target = "", single = "-> @to", multiple = "-> @to1, @to2"
|
|
236
|
+
let targetStr = "";
|
|
237
|
+
if (targets && targets.length > 0) {
|
|
238
|
+
targetStr = ` -> ${targets.map(t => `@${t}`).join(", ")}`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Escape newlines to preserve single-line format
|
|
242
|
+
const escapedContent = content.replace(/\n/g, "\\n");
|
|
243
|
+
|
|
244
|
+
const line = `[+${relativeSeconds}] [@${from}${targetStr}] ${escapedContent}`;
|
|
245
|
+
|
|
246
|
+
if (line.length > maxLength) {
|
|
247
|
+
// Calculate suffix first, then determine how much content to keep
|
|
248
|
+
// We want: kept_content + suffix <= maxLength
|
|
249
|
+
const truncatedChars = line.length - maxLength;
|
|
250
|
+
const suffix = `... [truncated ${truncatedChars} chars]`;
|
|
251
|
+
const keepLength = Math.max(0, maxLength - suffix.length);
|
|
252
|
+
return line.slice(0, keepLength) + suffix;
|
|
253
|
+
}
|
|
254
|
+
return line;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Serialize a Conversation object to a JSON-safe plain object
|
|
259
|
+
* Formats messages as a single multi-line string with relative timestamps
|
|
260
|
+
* Format: [+seconds] [@from -> @to] content
|
|
261
|
+
*/
|
|
262
|
+
function serializeConversation(
|
|
263
|
+
conversation: ConversationStore,
|
|
264
|
+
options: { includeToolResults?: boolean; untilId?: string } = {}
|
|
265
|
+
): Record<string, unknown> {
|
|
266
|
+
let messages = conversation.getAllMessages();
|
|
267
|
+
const pubkeyService = getPubkeyService();
|
|
268
|
+
|
|
269
|
+
// Filter messages up to and including untilId if provided
|
|
270
|
+
if (options.untilId) {
|
|
271
|
+
const untilIndex = messages.findIndex(msg => msg.eventId === options.untilId);
|
|
272
|
+
if (untilIndex === -1) {
|
|
273
|
+
// Message not found - return all messages as graceful fallback
|
|
274
|
+
logger.warn("untilId not found in conversation, returning all messages", {
|
|
275
|
+
untilId: options.untilId,
|
|
276
|
+
conversationId: conversation.id,
|
|
277
|
+
});
|
|
278
|
+
} else {
|
|
279
|
+
// Include messages up to and including the untilId message
|
|
280
|
+
messages = messages.slice(0, untilIndex + 1);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Find the first DEFINED timestamp to use as baseline for relative times.
|
|
285
|
+
// This handles edge cases where early messages (e.g., tool-calls synced via
|
|
286
|
+
// MessageSyncer) may lack timestamps. Using the first defined timestamp
|
|
287
|
+
// ensures later messages don't show huge epoch offsets.
|
|
288
|
+
let baselineTimestamp = 0;
|
|
289
|
+
for (const msg of messages) {
|
|
290
|
+
if (msg.timestamp !== undefined) {
|
|
291
|
+
baselineTimestamp = msg.timestamp;
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const formattedLines: string[] = [];
|
|
297
|
+
|
|
298
|
+
// Track the last known timestamp for fallback on entries without timestamps.
|
|
299
|
+
// This provides more accurate ordering than always falling back to baseline.
|
|
300
|
+
let lastKnownTimestamp = baselineTimestamp;
|
|
301
|
+
|
|
302
|
+
for (let i = 0; i < messages.length; i++) {
|
|
303
|
+
const entry = messages[i];
|
|
304
|
+
// Use lastKnownTimestamp as fallback when entry.timestamp is undefined.
|
|
305
|
+
// This ensures entries without timestamps (e.g., tool-calls synced via MessageSyncer)
|
|
306
|
+
// appear at their approximate position rather than showing [+0] or huge negative
|
|
307
|
+
// numbers like [+-1771103685].
|
|
308
|
+
const effectiveTimestamp = entry.timestamp ?? lastKnownTimestamp;
|
|
309
|
+
const relativeSeconds = Math.floor(effectiveTimestamp - baselineTimestamp);
|
|
310
|
+
|
|
311
|
+
// Update lastKnownTimestamp if this entry has a defined timestamp
|
|
312
|
+
if (entry.timestamp !== undefined) {
|
|
313
|
+
lastKnownTimestamp = entry.timestamp;
|
|
314
|
+
}
|
|
315
|
+
const from = pubkeyService.getNameSync(entry.pubkey);
|
|
316
|
+
const targets = entry.targetedPubkeys?.map(pk => pubkeyService.getNameSync(pk));
|
|
317
|
+
|
|
318
|
+
if (entry.messageType === "text") {
|
|
319
|
+
// Text messages: straightforward format
|
|
320
|
+
formattedLines.push(formatLine(relativeSeconds, from, targets, entry.content));
|
|
321
|
+
} else if (entry.messageType === "tool-call") {
|
|
322
|
+
// Only include tool calls if includeToolResults is true
|
|
323
|
+
if (!options.includeToolResults) {
|
|
324
|
+
// Skip tool call entries when not including tool results
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Tool call: look for matching tool-results by toolCallId or adjacency
|
|
329
|
+
const toolData = (entry.toolData ?? []) as ToolCallPart[];
|
|
330
|
+
|
|
331
|
+
// Check if we have toolCallIds to match with
|
|
332
|
+
const hasToolCallIds = toolData.some(tc => tc.toolCallId);
|
|
333
|
+
|
|
334
|
+
// Build a map of toolCallId -> result for matching (when IDs are present)
|
|
335
|
+
const toolResultsMap = new Map<string, ToolResultPart>();
|
|
336
|
+
// Also keep an ordered array for fallback adjacency matching
|
|
337
|
+
let adjacentResults: ToolResultPart[] = [];
|
|
338
|
+
let shouldSkipNext = false;
|
|
339
|
+
|
|
340
|
+
if (i + 1 < messages.length) {
|
|
341
|
+
const nextMsg = messages[i + 1];
|
|
342
|
+
if (nextMsg.messageType === "tool-result" && nextMsg.pubkey === entry.pubkey) {
|
|
343
|
+
const resultData = (nextMsg.toolData ?? []) as ToolResultPart[];
|
|
344
|
+
adjacentResults = resultData;
|
|
345
|
+
|
|
346
|
+
// Build toolCallId map for ID-based matching
|
|
347
|
+
for (const tr of resultData) {
|
|
348
|
+
if (tr.toolCallId) {
|
|
349
|
+
toolResultsMap.set(tr.toolCallId, tr);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Format tool calls with their matched results
|
|
356
|
+
const toolCallParts: string[] = [];
|
|
357
|
+
const matchedResultIds = new Set<string>();
|
|
358
|
+
let adjacentResultIndex = 0;
|
|
359
|
+
|
|
360
|
+
for (const tc of toolData) {
|
|
361
|
+
const toolName = tc.toolName || "unknown";
|
|
362
|
+
const input = tc.input !== undefined ? formatToolInput(tc.input) : "";
|
|
363
|
+
let toolCallStr = `[tool-use ${toolName} ${input}]`;
|
|
364
|
+
|
|
365
|
+
let matchingResult: ToolResultPart | undefined;
|
|
366
|
+
|
|
367
|
+
// Try to find matching result by toolCallId first
|
|
368
|
+
if (tc.toolCallId && toolResultsMap.has(tc.toolCallId)) {
|
|
369
|
+
matchingResult = toolResultsMap.get(tc.toolCallId);
|
|
370
|
+
matchedResultIds.add(tc.toolCallId);
|
|
371
|
+
} else if (!hasToolCallIds && adjacentResultIndex < adjacentResults.length) {
|
|
372
|
+
// Fallback: when no toolCallIds, match by position (adjacency)
|
|
373
|
+
matchingResult = adjacentResults[adjacentResultIndex++];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (matchingResult) {
|
|
377
|
+
const resultContent =
|
|
378
|
+
matchingResult.output !== undefined
|
|
379
|
+
? safeStringify(matchingResult.output)
|
|
380
|
+
: "";
|
|
381
|
+
toolCallStr += ` [tool-result ${resultContent}]`;
|
|
382
|
+
shouldSkipNext = true;
|
|
383
|
+
}
|
|
384
|
+
toolCallParts.push(toolCallStr);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Skip the next tool-result message if we merged all results
|
|
388
|
+
if (shouldSkipNext && i + 1 < messages.length) {
|
|
389
|
+
const nextMsg = messages[i + 1];
|
|
390
|
+
if (nextMsg.messageType === "tool-result" && nextMsg.pubkey === entry.pubkey) {
|
|
391
|
+
// For ID-based matching, verify all were matched
|
|
392
|
+
// For adjacency-based, we already processed them all
|
|
393
|
+
if (!hasToolCallIds || adjacentResults.every(tr => !tr.toolCallId || matchedResultIds.has(tr.toolCallId))) {
|
|
394
|
+
i++;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const content = toolCallParts.join(" ");
|
|
400
|
+
formattedLines.push(formatLine(relativeSeconds, from, targets, content));
|
|
401
|
+
} else if (entry.messageType === "tool-result") {
|
|
402
|
+
// Standalone tool-result (not merged with tool-call)
|
|
403
|
+
// Only show if includeToolResults is true
|
|
404
|
+
if (options.includeToolResults) {
|
|
405
|
+
const resultData = (entry.toolData ?? []) as ToolResultPart[];
|
|
406
|
+
const resultParts: string[] = [];
|
|
407
|
+
for (const tr of resultData) {
|
|
408
|
+
const resultContent =
|
|
409
|
+
tr.output !== undefined ? safeStringify(tr.output) : "";
|
|
410
|
+
resultParts.push(`[tool-result ${resultContent}]`);
|
|
411
|
+
}
|
|
412
|
+
formattedLines.push(formatLine(relativeSeconds, from, targets, resultParts.join(" ")));
|
|
413
|
+
}
|
|
414
|
+
} else if (entry.messageType === "delegation-marker") {
|
|
415
|
+
// Delegation markers: always shown (regardless of includeToolResults)
|
|
416
|
+
const marker = entry.delegationMarker;
|
|
417
|
+
|
|
418
|
+
// Validate required fields - skip gracefully if missing
|
|
419
|
+
if (!marker?.delegationConversationId || !marker?.recipientPubkey || !marker?.status) {
|
|
420
|
+
// Skip malformed delegation marker - don't crash, just omit from output
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const shortConversationId = marker.delegationConversationId.slice(0, PREFIX_LENGTH);
|
|
425
|
+
const recipientName = pubkeyService.getNameSync(marker.recipientPubkey);
|
|
426
|
+
|
|
427
|
+
// Format based on status
|
|
428
|
+
let emoji: string;
|
|
429
|
+
let statusText: string;
|
|
430
|
+
if (marker.status === "pending") {
|
|
431
|
+
emoji = "⏳";
|
|
432
|
+
statusText = "in progress";
|
|
433
|
+
} else if (marker.status === "completed") {
|
|
434
|
+
emoji = "✅";
|
|
435
|
+
statusText = "completed";
|
|
436
|
+
} else {
|
|
437
|
+
emoji = "⚠️";
|
|
438
|
+
statusText = "aborted";
|
|
439
|
+
}
|
|
440
|
+
const content = `${emoji} Delegation ${shortConversationId} → ${recipientName} ${statusText}`;
|
|
441
|
+
|
|
442
|
+
formattedLines.push(formatLine(relativeSeconds, from, targets, content));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
id: String(conversation.id),
|
|
448
|
+
title: conversation.title ? String(conversation.title) : undefined,
|
|
449
|
+
executionTime: safeCopy(conversation.executionTime),
|
|
450
|
+
messageCount: messages.length,
|
|
451
|
+
messages: formattedLines.join("\n"),
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Core implementation of conversation retrieval functionality
|
|
457
|
+
*/
|
|
458
|
+
async function executeConversationGet(
|
|
459
|
+
input: ConversationGetInput,
|
|
460
|
+
context: ToolExecutionContext
|
|
461
|
+
): Promise<ConversationGetOutput> {
|
|
462
|
+
if (!input.conversationId) {
|
|
463
|
+
return {
|
|
464
|
+
success: false,
|
|
465
|
+
message: "conversationId is required",
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Normalize conversation ID to full 64-char hex
|
|
470
|
+
const targetConversationId = normalizeEventId(input.conversationId);
|
|
471
|
+
if (!targetConversationId) {
|
|
472
|
+
return {
|
|
473
|
+
success: false,
|
|
474
|
+
message: `Could not resolve conversation ID "${input.conversationId}". Expected 64-char hex, 12-char hex prefix, or NIP-19 format (note1.../nevent1...).`,
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Normalize untilId if provided - graceful fallback for optional parameter
|
|
479
|
+
let targetUntilId: string | undefined = undefined;
|
|
480
|
+
if (input.untilId) {
|
|
481
|
+
const resolved = normalizeEventId(input.untilId);
|
|
482
|
+
targetUntilId = resolved ?? undefined;
|
|
483
|
+
if (!resolved) {
|
|
484
|
+
logger.warn("Could not resolve untilId, proceeding without filtering", {
|
|
485
|
+
untilId: input.untilId,
|
|
486
|
+
conversationId: targetConversationId,
|
|
487
|
+
agent: context.agent.name,
|
|
488
|
+
});
|
|
489
|
+
// Fall back to undefined - return unfiltered conversation
|
|
490
|
+
targetUntilId = undefined;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
logger.info("📖 Retrieving conversation", {
|
|
495
|
+
conversationId: targetConversationId,
|
|
496
|
+
isCurrentConversation: targetConversationId === context.conversationId,
|
|
497
|
+
agent: context.agent.name,
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
// Get conversation from ConversationStore
|
|
501
|
+
const conversation =
|
|
502
|
+
targetConversationId === context.conversationId
|
|
503
|
+
? context.getConversation()
|
|
504
|
+
: ConversationStore.get(targetConversationId);
|
|
505
|
+
|
|
506
|
+
if (!conversation) {
|
|
507
|
+
logger.info("📭 Conversation not found", {
|
|
508
|
+
conversationId: targetConversationId,
|
|
509
|
+
agent: context.agent.name,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
return {
|
|
513
|
+
success: false,
|
|
514
|
+
message: `Conversation ${targetConversationId} not found`,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
logger.info("✅ Conversation retrieved successfully", {
|
|
519
|
+
conversationId: conversation.id,
|
|
520
|
+
title: conversation.title,
|
|
521
|
+
messageCount: conversation.getMessageCount(),
|
|
522
|
+
untilId: targetUntilId,
|
|
523
|
+
agent: context.agent.name,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const serializedConversation = serializeConversation(conversation, {
|
|
527
|
+
includeToolResults: input.includeToolResults,
|
|
528
|
+
untilId: targetUntilId,
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
// If a prompt is provided, process the conversation through the LLM
|
|
532
|
+
if (input.prompt) {
|
|
533
|
+
try {
|
|
534
|
+
// Get LLM configuration - use summarization config if set, otherwise default
|
|
535
|
+
// Use getLLMConfig to resolve meta models automatically
|
|
536
|
+
const { llms } = await config.loadConfig();
|
|
537
|
+
const configName = llms.summarization || llms.default;
|
|
538
|
+
|
|
539
|
+
if (!configName) {
|
|
540
|
+
logger.warn("No LLM configuration available for conversation analysis");
|
|
541
|
+
return {
|
|
542
|
+
success: true,
|
|
543
|
+
conversation: serializedConversation,
|
|
544
|
+
message: "No LLM configuration available for prompt processing",
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const llmConfig = config.getLLMConfig(configName);
|
|
549
|
+
|
|
550
|
+
// Create LLM service
|
|
551
|
+
const llmService = llmServiceFactory.createService(llmConfig, {
|
|
552
|
+
agentName: "conversation-analyzer",
|
|
553
|
+
sessionId: `analyzer-${targetConversationId}`,
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Format conversation for LLM processing
|
|
557
|
+
const conversationText = JSON.stringify(serializedConversation, null, 2);
|
|
558
|
+
|
|
559
|
+
// Generate explanation using the LLM
|
|
560
|
+
const { object: result } = await llmService.generateObject(
|
|
561
|
+
[
|
|
562
|
+
{
|
|
563
|
+
role: "system",
|
|
564
|
+
content: `You are a helpful assistant that analyzes conversations and provides explanations based on user prompts.
|
|
565
|
+
|
|
566
|
+
CRITICAL REQUIREMENTS:
|
|
567
|
+
1. VERBATIM QUOTES: You MUST include relevant parts of the conversation verbatim in your response. Quote the exact messages that support your analysis.
|
|
568
|
+
2. PRESERVE IDENTIFIERS: All IDs, pubkeys, agent slugs, conversation IDs, event IDs, and other addressable data must be preserved exactly as they appear.
|
|
569
|
+
3. When referencing specific messages or participants, include their identifiers exactly as they appear.
|
|
570
|
+
4. Do not abbreviate or modify any identifiers - they are essential for traceability and reference.
|
|
571
|
+
5. Base your analysis ONLY on what is explicitly stated in the conversation.
|
|
572
|
+
|
|
573
|
+
FORMAT: Structure your response with:
|
|
574
|
+
- Your analysis/explanation
|
|
575
|
+
- Verbatim quotes from relevant messages (use quotation marks and attribute to the sender)
|
|
576
|
+
- All referenced identifiers preserved exactly
|
|
577
|
+
|
|
578
|
+
The user will provide a prompt describing what they want to know about the conversation.`,
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
role: "user",
|
|
582
|
+
content: `Please analyze the following conversation based on this prompt: "${input.prompt}"
|
|
583
|
+
|
|
584
|
+
IMPORTANT: Include verbatim quotes from the relevant parts of the conversation that support your analysis.
|
|
585
|
+
|
|
586
|
+
CONVERSATION DATA:
|
|
587
|
+
${conversationText}`,
|
|
588
|
+
},
|
|
589
|
+
],
|
|
590
|
+
z.object({
|
|
591
|
+
explanation: z
|
|
592
|
+
.string()
|
|
593
|
+
.describe(
|
|
594
|
+
"A detailed explanation based on the user's prompt. MUST include verbatim quotes from relevant messages and preserve all important identifiers."
|
|
595
|
+
),
|
|
596
|
+
})
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
logger.info("✅ Conversation analyzed with prompt", {
|
|
600
|
+
conversationId: conversation.id,
|
|
601
|
+
promptLength: input.prompt.length,
|
|
602
|
+
agent: context.agent.name,
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
return {
|
|
606
|
+
success: true,
|
|
607
|
+
conversation: serializedConversation,
|
|
608
|
+
explanation: result.explanation,
|
|
609
|
+
};
|
|
610
|
+
} catch (error) {
|
|
611
|
+
logger.error("Failed to process conversation with prompt", {
|
|
612
|
+
conversationId: conversation.id,
|
|
613
|
+
error: error instanceof Error ? error.message : String(error),
|
|
614
|
+
agent: context.agent.name,
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
return {
|
|
618
|
+
success: true,
|
|
619
|
+
conversation: serializedConversation,
|
|
620
|
+
message: `Failed to process prompt: ${error instanceof Error ? error.message : String(error)}`,
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
return {
|
|
626
|
+
success: true,
|
|
627
|
+
conversation: serializedConversation,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Create an AI SDK tool for retrieving conversations
|
|
633
|
+
*/
|
|
634
|
+
export function createConversationGetTool(context: ToolExecutionContext): AISdkTool {
|
|
635
|
+
const aiTool = tool({
|
|
636
|
+
description:
|
|
637
|
+
"Retrieve a conversation by its ID, including all messages/events in the conversation history. Returns conversation info (id, title, messageCount, executionTime) and a formatted messages string. Messages are formatted as: [+seconds] [@from -> @to] content, where seconds is relative to the first message. Tool calls and results can be merged into single lines when includeToolResults is true. Useful for reviewing conversation context, analyzing message history, or debugging agent interactions.",
|
|
638
|
+
|
|
639
|
+
inputSchema: conversationGetSchema,
|
|
640
|
+
|
|
641
|
+
execute: async (input: ConversationGetInput) => {
|
|
642
|
+
return await executeConversationGet(input, context);
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
Object.defineProperty(aiTool, "getHumanReadableContent", {
|
|
647
|
+
value: ({ conversationId, untilId, prompt }: ConversationGetInput) => {
|
|
648
|
+
const target = conversationId
|
|
649
|
+
? `conversation: ${conversationId}`
|
|
650
|
+
: "conversation (missing id)";
|
|
651
|
+
const upTo = untilId ? ` up to message ${untilId}` : "";
|
|
652
|
+
return prompt
|
|
653
|
+
? `Analyzing ${target}${upTo} with prompt`
|
|
654
|
+
: `Retrieving ${target}${upTo}`;
|
|
655
|
+
},
|
|
656
|
+
enumerable: false,
|
|
657
|
+
configurable: true,
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
return aiTool as AISdkTool;
|
|
661
|
+
}
|