@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,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for managing conversation execution time
|
|
3
|
+
* Works directly with ConversationStore objects, following DRY principle
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ConversationStore } from "./ConversationStore";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Start tracking execution time for a conversation
|
|
10
|
+
*/
|
|
11
|
+
export function startExecutionTime(conversation: ConversationStore): void {
|
|
12
|
+
if (conversation.executionTime.isActive) {
|
|
13
|
+
// Already active - don't restart
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
conversation.executionTime.currentSessionStart = Date.now();
|
|
18
|
+
conversation.executionTime.isActive = true;
|
|
19
|
+
conversation.executionTime.lastUpdated = Date.now();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Stop tracking execution time and add duration to total
|
|
24
|
+
* @returns Duration of this session in milliseconds
|
|
25
|
+
*/
|
|
26
|
+
export function stopExecutionTime(conversation: ConversationStore): number {
|
|
27
|
+
if (!conversation.executionTime.isActive || !conversation.executionTime.currentSessionStart) {
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const sessionDuration = Date.now() - conversation.executionTime.currentSessionStart;
|
|
32
|
+
const sessionSeconds = Math.round(sessionDuration / 1000);
|
|
33
|
+
|
|
34
|
+
conversation.executionTime.totalSeconds += sessionSeconds;
|
|
35
|
+
conversation.executionTime.currentSessionStart = undefined;
|
|
36
|
+
conversation.executionTime.isActive = false;
|
|
37
|
+
conversation.executionTime.lastUpdated = Date.now();
|
|
38
|
+
|
|
39
|
+
return sessionDuration;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get total execution time in seconds including any active session
|
|
44
|
+
*/
|
|
45
|
+
export function getTotalExecutionTimeSeconds(conversation: ConversationStore): number {
|
|
46
|
+
let total = conversation.executionTime.totalSeconds;
|
|
47
|
+
|
|
48
|
+
// Add current active session time if any
|
|
49
|
+
if (conversation.executionTime.isActive && conversation.executionTime.currentSessionStart) {
|
|
50
|
+
const activeSeconds = Math.round((Date.now() - conversation.executionTime.currentSessionStart) / 1000);
|
|
51
|
+
total += activeSeconds;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return total;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if execution is currently active
|
|
59
|
+
*/
|
|
60
|
+
export function isExecutionActive(conversation: ConversationStore): boolean {
|
|
61
|
+
return conversation.executionTime?.isActive ?? false;
|
|
62
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
interface DelegationResponse {
|
|
2
|
+
from: string;
|
|
3
|
+
content: string;
|
|
4
|
+
eventId: string;
|
|
5
|
+
status: "completed" | "error";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface DelegationInfo {
|
|
9
|
+
id: string;
|
|
10
|
+
from: string;
|
|
11
|
+
recipients: string[];
|
|
12
|
+
phase?: string;
|
|
13
|
+
message: string;
|
|
14
|
+
requestEventId: string;
|
|
15
|
+
responses: DelegationResponse[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Formats delegation information as structured XML for LLM comprehension
|
|
20
|
+
*/
|
|
21
|
+
export class DelegationXmlFormatter {
|
|
22
|
+
/**
|
|
23
|
+
* Render a delegation with its responses as XML
|
|
24
|
+
*/
|
|
25
|
+
static render(delegation: DelegationInfo, debug = false): string {
|
|
26
|
+
const eventPrefix = debug ? `[Event ${delegation.requestEventId.substring(0, 8)}] ` : "";
|
|
27
|
+
const recipientsAttr = delegation.recipients.join(",");
|
|
28
|
+
const phaseAttr = delegation.phase ? ` phase="${delegation.phase}"` : "";
|
|
29
|
+
|
|
30
|
+
let xml = `${eventPrefix}<delegation from="${delegation.from}" recipients="${recipientsAttr}" id="${delegation.id}"${phaseAttr}>`;
|
|
31
|
+
xml += `\n <delegation-request>${DelegationXmlFormatter.escapeXml(delegation.message)}</delegation-request>`;
|
|
32
|
+
|
|
33
|
+
// Add responses
|
|
34
|
+
for (const response of delegation.responses) {
|
|
35
|
+
const statusAttr = response.status === "error" ? ' error="true"' : "";
|
|
36
|
+
const eventIdAttr = debug ? ` event-id="${response.eventId.substring(0, 8)}"` : "";
|
|
37
|
+
xml += `\n <response from="${response.from}"${statusAttr}${eventIdAttr}>${DelegationXmlFormatter.escapeXml(response.content)}</response>`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Add pending placeholders for recipients without responses
|
|
41
|
+
const respondedFrom = new Set(delegation.responses.map((r) => r.from));
|
|
42
|
+
for (const recipient of delegation.recipients) {
|
|
43
|
+
if (!respondedFrom.has(recipient)) {
|
|
44
|
+
xml += `\n <response from="${recipient}" status="pending" />`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
xml += "\n</delegation>";
|
|
49
|
+
|
|
50
|
+
return xml;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Escape XML special characters
|
|
55
|
+
*/
|
|
56
|
+
private static escapeXml(text: string): string {
|
|
57
|
+
return text
|
|
58
|
+
.replace(/&/g, "&")
|
|
59
|
+
.replace(/</g, "<")
|
|
60
|
+
.replace(/>/g, ">")
|
|
61
|
+
.replace(/"/g, """)
|
|
62
|
+
.replace(/'/g, "'");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
2
|
+
import { MessageFormatter } from "./utils/MessageFormatter";
|
|
3
|
+
import { TimestampFormatter } from "./utils/TimestampFormatter";
|
|
4
|
+
import { TreeBuilder } from "./utils/TreeBuilder";
|
|
5
|
+
import { TreeRenderer } from "./utils/TreeRenderer";
|
|
6
|
+
|
|
7
|
+
export interface ThreadNode {
|
|
8
|
+
event: NDKEvent;
|
|
9
|
+
agent?: string; // Agent name/identifier
|
|
10
|
+
timestamp: Date;
|
|
11
|
+
content: string;
|
|
12
|
+
toolCall?: {
|
|
13
|
+
name: string;
|
|
14
|
+
args?: string;
|
|
15
|
+
};
|
|
16
|
+
children: ThreadNode[];
|
|
17
|
+
depth: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface FormatterOptions {
|
|
21
|
+
includeTimestamps: boolean;
|
|
22
|
+
timestampFormat: "relative" | "absolute" | "time-only";
|
|
23
|
+
maxDepth?: number;
|
|
24
|
+
includeToolCalls: boolean;
|
|
25
|
+
treeStyle: "ascii" | "unicode" | "markdown";
|
|
26
|
+
compactMode: boolean; // Single-line per message
|
|
27
|
+
currentAgentPubkey?: string; // The agent we're formatting for (to show "you")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class ThreadedConversationFormatter {
|
|
31
|
+
private treeBuilder: TreeBuilder;
|
|
32
|
+
private messageFormatter: MessageFormatter;
|
|
33
|
+
private timestampFormatter: TimestampFormatter;
|
|
34
|
+
private treeRenderer: TreeRenderer;
|
|
35
|
+
|
|
36
|
+
constructor() {
|
|
37
|
+
this.treeBuilder = new TreeBuilder();
|
|
38
|
+
this.messageFormatter = new MessageFormatter();
|
|
39
|
+
this.timestampFormatter = new TimestampFormatter();
|
|
40
|
+
this.treeRenderer = new TreeRenderer();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Build tree structure from flat event list
|
|
45
|
+
*/
|
|
46
|
+
async buildThreadTree(events: NDKEvent[]): Promise<ThreadNode[]> {
|
|
47
|
+
return this.treeBuilder.buildFromEvents(events);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Format single thread as ASCII/Unicode tree
|
|
52
|
+
*/
|
|
53
|
+
formatThread(root: ThreadNode, options?: FormatterOptions): string {
|
|
54
|
+
const opts = this.getDefaultOptions(options);
|
|
55
|
+
return this.renderNode(root, opts, "", true);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Extract agent-specific participation branches
|
|
60
|
+
*/
|
|
61
|
+
extractAgentBranches(tree: ThreadNode[], agentPubkey: string): ThreadNode[] {
|
|
62
|
+
const relevantNodes: ThreadNode[] = [];
|
|
63
|
+
|
|
64
|
+
for (const root of tree) {
|
|
65
|
+
const extractedBranch = this.extractRelevantBranch(root, agentPubkey);
|
|
66
|
+
if (extractedBranch) {
|
|
67
|
+
relevantNodes.push(extractedBranch);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return relevantNodes;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Format branches where the agent participated, excluding the active branch
|
|
76
|
+
* This is the main entry point for getting "other threads" context
|
|
77
|
+
*/
|
|
78
|
+
public async formatOtherBranches(
|
|
79
|
+
allEvents: NDKEvent[],
|
|
80
|
+
agentPubkey: string,
|
|
81
|
+
activeBranchIds: Set<string>
|
|
82
|
+
): Promise<string | null> {
|
|
83
|
+
// 1. Build complete conversation tree from all events
|
|
84
|
+
const completeTree = await this.buildThreadTree(allEvents);
|
|
85
|
+
|
|
86
|
+
// 2. Prune the active branch from the tree
|
|
87
|
+
const prunedTree = this.pruneBranch(completeTree, activeBranchIds);
|
|
88
|
+
|
|
89
|
+
// 3. Extract branches where agent participated from the pruned tree
|
|
90
|
+
const agentBranches = this.extractRelevantBranches(prunedTree, agentPubkey);
|
|
91
|
+
|
|
92
|
+
// 4. If no relevant branches found, return null
|
|
93
|
+
if (agentBranches.length === 0) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 5. Format the branches into a string
|
|
98
|
+
const options: FormatterOptions = {
|
|
99
|
+
includeTimestamps: true,
|
|
100
|
+
timestampFormat: "time-only",
|
|
101
|
+
includeToolCalls: true,
|
|
102
|
+
treeStyle: "ascii",
|
|
103
|
+
compactMode: true,
|
|
104
|
+
currentAgentPubkey: agentPubkey, // Pass the agent we're formatting for
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const result: string[] = [];
|
|
108
|
+
for (let i = 0; i < agentBranches.length; i++) {
|
|
109
|
+
if (i > 0) {
|
|
110
|
+
result.push(`\n${"─".repeat(60)}\n`);
|
|
111
|
+
}
|
|
112
|
+
result.push(this.formatThread(agentBranches[i], options));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return result.join("\n");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Remove all nodes that are part of the active branch
|
|
120
|
+
* Returns a new tree with the active branch pruned out
|
|
121
|
+
*/
|
|
122
|
+
private pruneBranch(tree: ThreadNode[], activeBranchIds: Set<string>): ThreadNode[] {
|
|
123
|
+
const prunedRoots: ThreadNode[] = [];
|
|
124
|
+
|
|
125
|
+
for (const root of tree) {
|
|
126
|
+
const prunedNode = this.pruneNode(root, activeBranchIds);
|
|
127
|
+
if (prunedNode) {
|
|
128
|
+
prunedRoots.push(prunedNode);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return prunedRoots;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Recursively prune a node and its children
|
|
137
|
+
* Returns null if the entire subtree should be removed
|
|
138
|
+
*/
|
|
139
|
+
private pruneNode(node: ThreadNode, activeBranchIds: Set<string>): ThreadNode | null {
|
|
140
|
+
// If this node is in the active branch
|
|
141
|
+
if (activeBranchIds.has(node.event.id)) {
|
|
142
|
+
// Check if ANY child is NOT in the active branch
|
|
143
|
+
// If so, we need to keep this node but prune only active children
|
|
144
|
+
const hasNonActiveBranches = node.children.some(
|
|
145
|
+
(child) => !this.isEntireBranchActive(child, activeBranchIds)
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
if (hasNonActiveBranches) {
|
|
149
|
+
// Keep this node but prune children selectively
|
|
150
|
+
const prunedChildren: ThreadNode[] = [];
|
|
151
|
+
for (const child of node.children) {
|
|
152
|
+
const prunedChild = this.pruneNode(child, activeBranchIds);
|
|
153
|
+
if (prunedChild) {
|
|
154
|
+
prunedChildren.push(prunedChild);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Return the node with only non-active children
|
|
159
|
+
return {
|
|
160
|
+
...node,
|
|
161
|
+
children: prunedChildren,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// This node and ALL its descendants are in the active branch
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Node is not in active branch - keep it and recursively prune children
|
|
169
|
+
const prunedChildren: ThreadNode[] = [];
|
|
170
|
+
for (const child of node.children) {
|
|
171
|
+
const prunedChild = this.pruneNode(child, activeBranchIds);
|
|
172
|
+
if (prunedChild) {
|
|
173
|
+
prunedChildren.push(prunedChild);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Return the node with pruned children
|
|
178
|
+
return {
|
|
179
|
+
...node,
|
|
180
|
+
children: prunedChildren,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if an entire branch (node and all descendants) is in the active branch
|
|
186
|
+
*/
|
|
187
|
+
private isEntireBranchActive(node: ThreadNode, activeBranchIds: Set<string>): boolean {
|
|
188
|
+
if (!activeBranchIds.has(node.event.id)) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check all children recursively
|
|
193
|
+
for (const child of node.children) {
|
|
194
|
+
if (!this.isEntireBranchActive(child, activeBranchIds)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Extract branches where the agent participated (similar to extractAgentBranches)
|
|
204
|
+
* but works on already pruned tree
|
|
205
|
+
*/
|
|
206
|
+
private extractRelevantBranches(tree: ThreadNode[], agentPubkey: string): ThreadNode[] {
|
|
207
|
+
const relevantNodes: ThreadNode[] = [];
|
|
208
|
+
|
|
209
|
+
for (const root of tree) {
|
|
210
|
+
const extractedBranch = this.extractRelevantBranch(root, agentPubkey);
|
|
211
|
+
if (extractedBranch) {
|
|
212
|
+
relevantNodes.push(extractedBranch);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return relevantNodes;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private extractRelevantBranch(node: ThreadNode, agentPubkey: string): ThreadNode | null {
|
|
220
|
+
// Check if this node or any descendant involves the agent
|
|
221
|
+
const involvedInBranch = this.isAgentInvolvedInBranch(node, agentPubkey);
|
|
222
|
+
|
|
223
|
+
if (!involvedInBranch) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// If agent is involved in this branch, return the ENTIRE branch with all descendants
|
|
228
|
+
// This preserves full context of conversations the agent participated in
|
|
229
|
+
return this.cloneNode(node);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private cloneNode(node: ThreadNode): ThreadNode {
|
|
233
|
+
return {
|
|
234
|
+
...node,
|
|
235
|
+
children: node.children.map((child) => this.cloneNode(child)),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private isAgentInvolvedInBranch(node: ThreadNode, agentPubkey: string): boolean {
|
|
240
|
+
// Check if this node is from the agent
|
|
241
|
+
if (node.event.pubkey === agentPubkey) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Check if any child branch has agent involvement
|
|
246
|
+
for (const child of node.children) {
|
|
247
|
+
if (this.isAgentInvolvedInBranch(child, agentPubkey)) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private renderNode(
|
|
256
|
+
node: ThreadNode,
|
|
257
|
+
options: FormatterOptions,
|
|
258
|
+
prefix: string,
|
|
259
|
+
isLast: boolean
|
|
260
|
+
): string {
|
|
261
|
+
const lines: string[] = [];
|
|
262
|
+
|
|
263
|
+
// Format the current node
|
|
264
|
+
const message = this.messageFormatter.format(node, options);
|
|
265
|
+
const timestamp = options.includeTimestamps
|
|
266
|
+
? this.timestampFormatter.format(node.timestamp, options.timestampFormat)
|
|
267
|
+
: "";
|
|
268
|
+
|
|
269
|
+
// Add "(you)" if this is the current agent
|
|
270
|
+
let agentName = node.agent || "Unknown";
|
|
271
|
+
if (options.currentAgentPubkey && node.event.pubkey === options.currentAgentPubkey) {
|
|
272
|
+
agentName = `${agentName} (you)`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const connector = this.treeRenderer.getConnector(options.treeStyle, isLast);
|
|
276
|
+
|
|
277
|
+
// Handle multi-line messages by joining with ⏎ separator
|
|
278
|
+
const inlineMessage = message.replace(/\n/g, " ⏎ ");
|
|
279
|
+
lines.push(`${prefix}${connector}${agentName}${timestamp}: ${inlineMessage}`);
|
|
280
|
+
|
|
281
|
+
// Render children with appropriate prefixes
|
|
282
|
+
const childPrefix = prefix + this.treeRenderer.getChildPrefix(options.treeStyle, isLast);
|
|
283
|
+
if (node.children.length > 0 && (!options.maxDepth || node.depth < options.maxDepth)) {
|
|
284
|
+
for (const [index, child] of node.children.entries()) {
|
|
285
|
+
const isLastChild = index === node.children.length - 1;
|
|
286
|
+
lines.push(this.renderNode(child, options, childPrefix, isLastChild));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return lines.join("\n");
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private getDefaultOptions(options?: Partial<FormatterOptions>): FormatterOptions {
|
|
294
|
+
return {
|
|
295
|
+
includeTimestamps: true,
|
|
296
|
+
timestampFormat: "time-only",
|
|
297
|
+
includeToolCalls: true,
|
|
298
|
+
treeStyle: "ascii",
|
|
299
|
+
compactMode: true,
|
|
300
|
+
...options,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ThreadedConversationFormatter,
|
|
3
|
+
type ThreadNode,
|
|
4
|
+
type FormatterOptions,
|
|
5
|
+
} from "./ThreadedConversationFormatter";
|
|
6
|
+
export { TreeBuilder } from "./utils/TreeBuilder";
|
|
7
|
+
export { MessageFormatter } from "./utils/MessageFormatter";
|
|
8
|
+
export { TimestampFormatter } from "./utils/TimestampFormatter";
|
|
9
|
+
export { TreeRenderer } from "./utils/TreeRenderer";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { FormatterOptions, ThreadNode } from "../ThreadedConversationFormatter";
|
|
2
|
+
|
|
3
|
+
export class MessageFormatter {
|
|
4
|
+
/**
|
|
5
|
+
* Format a message node according to options
|
|
6
|
+
*/
|
|
7
|
+
format(node: ThreadNode, options: FormatterOptions): string {
|
|
8
|
+
let message = node.content;
|
|
9
|
+
|
|
10
|
+
// Truncate if in compact mode
|
|
11
|
+
if (options.compactMode) {
|
|
12
|
+
message = this.truncateMessage(message, 100);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Add tool call information if present
|
|
16
|
+
if (options.includeToolCalls && node.toolCall) {
|
|
17
|
+
const toolCallStr = node.toolCall.args
|
|
18
|
+
? `[calls tool: ${node.toolCall.name}(${this.truncateMessage(node.toolCall.args, 50)})]`
|
|
19
|
+
: `[calls tool: ${node.toolCall.name}]`;
|
|
20
|
+
message = `${toolCallStr} ${message}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Clean up message for single-line display
|
|
24
|
+
if (options.compactMode) {
|
|
25
|
+
message = message.replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return message;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private truncateMessage(text: string, maxLength: number): string {
|
|
32
|
+
if (text.length <= maxLength) {
|
|
33
|
+
return text;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Try to truncate at a word boundary
|
|
37
|
+
const truncated = text.substring(0, maxLength);
|
|
38
|
+
const lastSpace = truncated.lastIndexOf(" ");
|
|
39
|
+
|
|
40
|
+
if (lastSpace > maxLength * 0.7) {
|
|
41
|
+
return `${truncated.substring(0, lastSpace)}...`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return `${truncated}...`;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export class TimestampFormatter {
|
|
2
|
+
/**
|
|
3
|
+
* Format a timestamp according to the specified format
|
|
4
|
+
*/
|
|
5
|
+
format(timestamp: Date, format: "relative" | "absolute" | "time-only"): string {
|
|
6
|
+
switch (format) {
|
|
7
|
+
case "relative":
|
|
8
|
+
return this.formatRelative(timestamp);
|
|
9
|
+
case "absolute":
|
|
10
|
+
return this.formatAbsolute(timestamp);
|
|
11
|
+
case "time-only":
|
|
12
|
+
return this.formatTimeOnly(timestamp);
|
|
13
|
+
default:
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private formatRelative(timestamp: Date): string {
|
|
19
|
+
const now = new Date();
|
|
20
|
+
const diffMs = now.getTime() - timestamp.getTime();
|
|
21
|
+
const diffSec = Math.floor(diffMs / 1000);
|
|
22
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
23
|
+
const diffHour = Math.floor(diffMin / 60);
|
|
24
|
+
const diffDay = Math.floor(diffHour / 24);
|
|
25
|
+
|
|
26
|
+
if (diffSec < 60) {
|
|
27
|
+
return ` [${diffSec}s ago]`;
|
|
28
|
+
}
|
|
29
|
+
if (diffMin < 60) {
|
|
30
|
+
return ` [${diffMin}m ago]`;
|
|
31
|
+
}
|
|
32
|
+
if (diffHour < 24) {
|
|
33
|
+
return ` [${diffHour}h ago]`;
|
|
34
|
+
}
|
|
35
|
+
return ` [${diffDay}d ago]`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private formatAbsolute(timestamp: Date): string {
|
|
39
|
+
const year = timestamp.getFullYear();
|
|
40
|
+
const month = String(timestamp.getMonth() + 1).padStart(2, "0");
|
|
41
|
+
const day = String(timestamp.getDate()).padStart(2, "0");
|
|
42
|
+
const hour = String(timestamp.getHours()).padStart(2, "0");
|
|
43
|
+
const minute = String(timestamp.getMinutes()).padStart(2, "0");
|
|
44
|
+
const second = String(timestamp.getSeconds()).padStart(2, "0");
|
|
45
|
+
|
|
46
|
+
return ` [${year}-${month}-${day} ${hour}:${minute}:${second}]`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private formatTimeOnly(timestamp: Date): string {
|
|
50
|
+
const hour = String(timestamp.getHours()).padStart(2, "0");
|
|
51
|
+
const minute = String(timestamp.getMinutes()).padStart(2, "0");
|
|
52
|
+
const second = String(timestamp.getSeconds()).padStart(2, "0");
|
|
53
|
+
|
|
54
|
+
return ` [${hour}:${minute}:${second}]`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { getPubkeyService } from "@/services/PubkeyService";
|
|
2
|
+
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
3
|
+
import type { ThreadNode } from "../ThreadedConversationFormatter";
|
|
4
|
+
|
|
5
|
+
export class TreeBuilder {
|
|
6
|
+
/**
|
|
7
|
+
* Build tree structure from flat event list
|
|
8
|
+
*/
|
|
9
|
+
async buildFromEvents(events: NDKEvent[]): Promise<ThreadNode[]> {
|
|
10
|
+
if (events.length === 0) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Create node map indexed by event ID
|
|
15
|
+
const nodeMap = new Map<string, ThreadNode>();
|
|
16
|
+
const rootNodes: ThreadNode[] = [];
|
|
17
|
+
|
|
18
|
+
// First pass: create all nodes
|
|
19
|
+
for (const event of events) {
|
|
20
|
+
const node: ThreadNode = {
|
|
21
|
+
event,
|
|
22
|
+
agent: await this.extractAgentName(event),
|
|
23
|
+
timestamp: new Date((event.created_at ?? 0) * 1000),
|
|
24
|
+
content: event.content,
|
|
25
|
+
toolCall: this.extractToolCall(event),
|
|
26
|
+
children: [],
|
|
27
|
+
depth: 0,
|
|
28
|
+
};
|
|
29
|
+
nodeMap.set(event.id, node);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Second pass: establish parent-child relationships
|
|
33
|
+
for (const event of events) {
|
|
34
|
+
const node = nodeMap.get(event.id);
|
|
35
|
+
if (!node) continue;
|
|
36
|
+
|
|
37
|
+
const parentId = this.findParentEventId(event);
|
|
38
|
+
|
|
39
|
+
if (parentId && nodeMap.has(parentId)) {
|
|
40
|
+
const parentNode = nodeMap.get(parentId);
|
|
41
|
+
if (parentNode) {
|
|
42
|
+
parentNode.children.push(node);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
// No parent found, this is a root node
|
|
46
|
+
rootNodes.push(node);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Third pass: calculate depths and sort children
|
|
51
|
+
for (const root of rootNodes) {
|
|
52
|
+
this.calculateDepthsAndSort(root, 0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Sort root nodes by timestamp
|
|
56
|
+
rootNodes.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
57
|
+
|
|
58
|
+
return rootNodes;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private findParentEventId(event: NDKEvent): string | null {
|
|
62
|
+
// Look for 'e' tags that indicate a reply
|
|
63
|
+
const eTags = event.tags.filter((tag) => tag[0] === "e");
|
|
64
|
+
|
|
65
|
+
// The convention is that the first 'e' tag is the root,
|
|
66
|
+
// and the last 'e' tag is the direct parent (if multiple e tags)
|
|
67
|
+
if (eTags.length === 1) {
|
|
68
|
+
return eTags[0][1]; // Single e tag is the parent
|
|
69
|
+
}
|
|
70
|
+
if (eTags.length > 1) {
|
|
71
|
+
// Check for 'reply' marker
|
|
72
|
+
const replyTag = eTags.find((tag) => tag[3] === "reply");
|
|
73
|
+
if (replyTag) {
|
|
74
|
+
return replyTag[1];
|
|
75
|
+
}
|
|
76
|
+
// Otherwise, last e tag is typically the direct parent
|
|
77
|
+
return eTags[eTags.length - 1][1];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private async extractAgentName(event: NDKEvent): Promise<string | undefined> {
|
|
84
|
+
// Use the PubkeyService to resolve the actual name
|
|
85
|
+
const nameRepo = getPubkeyService();
|
|
86
|
+
const name = await nameRepo.getName(event.pubkey);
|
|
87
|
+
return name;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private extractToolCall(event: NDKEvent): { name: string; args?: string } | undefined {
|
|
91
|
+
// Look for tool call indicators in tags
|
|
92
|
+
const toolTag = event.tags.find((tag) => tag[0] === "tool");
|
|
93
|
+
if (toolTag) {
|
|
94
|
+
return {
|
|
95
|
+
name: toolTag[1],
|
|
96
|
+
args: toolTag[2],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Check if content indicates a tool call - multiple patterns
|
|
101
|
+
const patterns = [
|
|
102
|
+
/(?:calls tool|using tool|executing):\s*(\w+)(?:\(([^)]*)\))?/i,
|
|
103
|
+
/(?:calling tool|call):\s*(\w+)(?:\(([^)]*)\))?/i,
|
|
104
|
+
/(?:Now|now)\s+calling\s+tool:\s*(\w+)(?:\(([^)]*)\))?/i,
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
for (const pattern of patterns) {
|
|
108
|
+
const match = event.content.match(pattern);
|
|
109
|
+
if (match) {
|
|
110
|
+
return {
|
|
111
|
+
name: match[1],
|
|
112
|
+
args: match[2],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private calculateDepthsAndSort(node: ThreadNode, depth: number): void {
|
|
121
|
+
node.depth = depth;
|
|
122
|
+
|
|
123
|
+
// Sort children by timestamp
|
|
124
|
+
node.children.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
125
|
+
|
|
126
|
+
// Recursively process children
|
|
127
|
+
for (const child of node.children) {
|
|
128
|
+
this.calculateDepthsAndSort(child, depth + 1);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|