@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
package/src/index.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// TENEX CLI Entry Point
|
|
4
|
+
// This is a CLI application - NOT a library. Zero exports.
|
|
5
|
+
|
|
6
|
+
// MUST BE FIRST - Initialize OpenTelemetry before any other imports
|
|
7
|
+
// Read config synchronously to check telemetry setting before importing anything else
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { initializeTelemetry } from "@/telemetry/setup";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get the base TENEX directory path for early initialization.
|
|
15
|
+
* Respects TENEX_BASE_DIR environment variable for running multiple isolated instances.
|
|
16
|
+
* This is a minimal inline version used before other imports are available.
|
|
17
|
+
*/
|
|
18
|
+
function getBasePath(): string {
|
|
19
|
+
return process.env.TENEX_BASE_DIR || join(homedir(), ".tenex");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface TelemetryConfig {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
serviceName: string;
|
|
25
|
+
endpoint: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getTelemetryConfig(): TelemetryConfig {
|
|
29
|
+
const configPath = join(getBasePath(), "config.json");
|
|
30
|
+
const defaults: TelemetryConfig = {
|
|
31
|
+
enabled: true,
|
|
32
|
+
serviceName: "tenex-daemon",
|
|
33
|
+
endpoint: "http://localhost:4318/v1/traces",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
if (!existsSync(configPath)) return defaults;
|
|
37
|
+
try {
|
|
38
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
39
|
+
return {
|
|
40
|
+
enabled: config.telemetry?.enabled !== false,
|
|
41
|
+
serviceName: config.telemetry?.serviceName || defaults.serviceName,
|
|
42
|
+
endpoint: config.telemetry?.endpoint || defaults.endpoint,
|
|
43
|
+
};
|
|
44
|
+
} catch (error) {
|
|
45
|
+
// Issue #6: Make config parse errors visible to users
|
|
46
|
+
console.warn(`[TENEX] Warning: Failed to parse config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
47
|
+
return defaults; // default on parse error
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Initialize telemetry before any other imports
|
|
52
|
+
const telemetryConfig = getTelemetryConfig();
|
|
53
|
+
initializeTelemetry(telemetryConfig.enabled, telemetryConfig.serviceName, telemetryConfig.endpoint);
|
|
54
|
+
|
|
55
|
+
// Main execution - all imports happen AFTER telemetry is initialized
|
|
56
|
+
// This ensures OpenTelemetry can properly instrument all imported modules
|
|
57
|
+
async function main(): Promise<void> {
|
|
58
|
+
// Issue #1 & #4: Dynamic imports after telemetry initialization, using @/ aliases
|
|
59
|
+
const [
|
|
60
|
+
{ Command },
|
|
61
|
+
{ getHeuristicEngine, getDefaultHeuristics },
|
|
62
|
+
{ daemonCommand },
|
|
63
|
+
{ setupCommand },
|
|
64
|
+
{ doctorCommand },
|
|
65
|
+
{ agentCommand },
|
|
66
|
+
{ handleCliError },
|
|
67
|
+
] = await Promise.all([
|
|
68
|
+
import("commander"),
|
|
69
|
+
import("@/services/heuristics"),
|
|
70
|
+
import("@/commands/daemon"),
|
|
71
|
+
import("@/commands/setup/index"),
|
|
72
|
+
import("@/commands/doctor"),
|
|
73
|
+
import("@/commands/agent"),
|
|
74
|
+
import("@/utils/cli-error"),
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
// Initialize heuristics system with default rules
|
|
78
|
+
const heuristicEngine = getHeuristicEngine({
|
|
79
|
+
debug: process.env.DEBUG_HEURISTICS === "true",
|
|
80
|
+
});
|
|
81
|
+
for (const heuristic of getDefaultHeuristics()) {
|
|
82
|
+
heuristicEngine.register(heuristic);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// CLI setup
|
|
86
|
+
const program = new Command();
|
|
87
|
+
|
|
88
|
+
// Issue #5: Use npm_package_version with fallback
|
|
89
|
+
program
|
|
90
|
+
.name("tenex")
|
|
91
|
+
.description("TENEX Command Line Interface")
|
|
92
|
+
.version(process.env.npm_package_version || "0.8.0");
|
|
93
|
+
|
|
94
|
+
// Register subcommands
|
|
95
|
+
program.addCommand(daemonCommand);
|
|
96
|
+
program.addCommand(setupCommand);
|
|
97
|
+
program.addCommand(doctorCommand);
|
|
98
|
+
program.addCommand(agentCommand);
|
|
99
|
+
|
|
100
|
+
// Issue #2: Enable exitOverride so errors are thrown instead of calling process.exit
|
|
101
|
+
program.exitOverride();
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
// Issue #2: Use parseAsync to properly catch async command errors
|
|
105
|
+
await program.parseAsync(process.argv);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
// Commander throws CommanderError for --help, --version, and actual errors
|
|
108
|
+
// Check if it's a "help" or "version" exit - these are not real errors
|
|
109
|
+
if (
|
|
110
|
+
error instanceof Error &&
|
|
111
|
+
"code" in error &&
|
|
112
|
+
(error.code === "commander.helpDisplayed" || error.code === "commander.version")
|
|
113
|
+
) {
|
|
114
|
+
// Normal exit for help/version - exit cleanly
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
117
|
+
handleCliError(error, "Fatal error in TENEX CLI");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Execute CLI - this is an application, not a library
|
|
122
|
+
main().catch((error) => {
|
|
123
|
+
// Fallback error handler for errors during dynamic imports
|
|
124
|
+
console.error("Fatal error during TENEX CLI initialization:", error);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
});
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { mkdirSync, readdirSync, realpathSync, existsSync, lstatSync, openSync, readSync, closeSync, constants as fsConstants } from "node:fs";
|
|
2
|
+
import { isAbsolute, join, normalize, relative, resolve, dirname } from "node:path";
|
|
3
|
+
import { getTenexBasePath } from "@/constants";
|
|
4
|
+
import { logger } from "@/utils/logger";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Error thrown when a path escapes the agent's home directory scope.
|
|
8
|
+
*/
|
|
9
|
+
export class HomeScopeViolationError extends Error {
|
|
10
|
+
constructor(message: string) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "HomeScopeViolationError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Maximum number of +prefixed files to inject into system prompt.
|
|
18
|
+
* Prevents prompt bloat if an agent creates many files.
|
|
19
|
+
*/
|
|
20
|
+
const MAX_INJECTED_FILES = 10;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Maximum content length for injected files before truncation.
|
|
24
|
+
*/
|
|
25
|
+
const MAX_INJECTED_FILE_LENGTH = 1500;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Represents a file to be injected into the agent's system prompt.
|
|
29
|
+
*/
|
|
30
|
+
export interface InjectedFile {
|
|
31
|
+
filename: string;
|
|
32
|
+
content: string;
|
|
33
|
+
truncated: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the short pubkey (first 8 characters) for an agent.
|
|
38
|
+
*/
|
|
39
|
+
function getShortPubkey(pubkey: string): string {
|
|
40
|
+
return pubkey.slice(0, 8);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the home directory path for an agent.
|
|
45
|
+
* This is the canonical source of truth for agent home directory paths.
|
|
46
|
+
*/
|
|
47
|
+
export function getAgentHomeDirectory(agentPubkey: string): string {
|
|
48
|
+
const shortPubkey = getShortPubkey(agentPubkey);
|
|
49
|
+
return join(getTenexBasePath(), "home", shortPubkey);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Normalize and resolve a path to prevent path traversal attacks.
|
|
54
|
+
* Resolves .., ., and normalizes the path to an absolute form.
|
|
55
|
+
* @param inputPath - The path to normalize (should be absolute)
|
|
56
|
+
* @returns The normalized absolute path
|
|
57
|
+
*/
|
|
58
|
+
export function normalizePath(inputPath: string): string {
|
|
59
|
+
// Resolve handles .., ., and makes the path absolute
|
|
60
|
+
const resolved = resolve(inputPath);
|
|
61
|
+
// Normalize handles redundant separators
|
|
62
|
+
return normalize(resolved);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Safely call realpathSync, returning null if it fails due to permissions or other errors.
|
|
67
|
+
*/
|
|
68
|
+
function safeRealpathSync(path: string): string | null {
|
|
69
|
+
try {
|
|
70
|
+
return realpathSync(path);
|
|
71
|
+
} catch {
|
|
72
|
+
// Permission denied, or other errors - fall back to normalized path
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resolve the real path of a file or directory, following symlinks.
|
|
79
|
+
* If the path doesn't exist, resolves the parent directory and appends the filename.
|
|
80
|
+
* This ensures symlinks are resolved even for files that will be created.
|
|
81
|
+
* Falls back to normalized path if realpath resolution fails (e.g., permission denied).
|
|
82
|
+
* @param inputPath - The path to resolve
|
|
83
|
+
* @returns The resolved real path with symlinks followed
|
|
84
|
+
*/
|
|
85
|
+
function resolveRealPath(inputPath: string): string {
|
|
86
|
+
const normalized = normalizePath(inputPath);
|
|
87
|
+
|
|
88
|
+
// If the path exists, resolve it directly
|
|
89
|
+
if (existsSync(normalized)) {
|
|
90
|
+
const realPath = safeRealpathSync(normalized);
|
|
91
|
+
return realPath ?? normalized;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Path doesn't exist - resolve the parent directory instead
|
|
95
|
+
// This catches symlinks in the parent chain
|
|
96
|
+
const parentDir = dirname(normalized);
|
|
97
|
+
const filename = normalized.slice(parentDir.length + 1); // Get the filename portion
|
|
98
|
+
|
|
99
|
+
if (existsSync(parentDir)) {
|
|
100
|
+
const realParent = safeRealpathSync(parentDir);
|
|
101
|
+
return realParent ? join(realParent, filename) : normalized;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Neither path nor parent exists - walk up until we find an existing ancestor
|
|
105
|
+
let currentPath = parentDir;
|
|
106
|
+
const pathParts: string[] = [filename];
|
|
107
|
+
|
|
108
|
+
while (currentPath && currentPath !== dirname(currentPath)) {
|
|
109
|
+
const parent = dirname(currentPath);
|
|
110
|
+
pathParts.unshift(currentPath.slice(parent.length + 1));
|
|
111
|
+
currentPath = parent;
|
|
112
|
+
|
|
113
|
+
if (existsSync(currentPath)) {
|
|
114
|
+
const realAncestor = safeRealpathSync(currentPath);
|
|
115
|
+
return realAncestor ? join(realAncestor, ...pathParts) : normalized;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// No ancestor exists, return normalized path as-is
|
|
120
|
+
return normalized;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Check if a path is within a given directory.
|
|
125
|
+
* Both paths are normalized and symlinks are resolved to prevent escape attacks.
|
|
126
|
+
* Uses path.relative for cross-platform separator handling.
|
|
127
|
+
* @param inputPath - The path to check (will be normalized, symlinks resolved)
|
|
128
|
+
* @param directory - The directory to check against (will be normalized, symlinks resolved)
|
|
129
|
+
* @returns true if path is within or equal to directory
|
|
130
|
+
*/
|
|
131
|
+
export function isPathWithinDirectory(inputPath: string, directory: string): boolean {
|
|
132
|
+
// Resolve symlinks to prevent symlink escape attacks
|
|
133
|
+
const realPath = resolveRealPath(inputPath);
|
|
134
|
+
const realDir = resolveRealPath(directory);
|
|
135
|
+
|
|
136
|
+
// Use path.relative for cross-platform handling
|
|
137
|
+
// If the relative path starts with ".." or is absolute, the path is outside
|
|
138
|
+
const relativePath = relative(realDir, realPath);
|
|
139
|
+
|
|
140
|
+
// Path is within directory if:
|
|
141
|
+
// 1. It doesn't start with ".." (would mean escaping the directory)
|
|
142
|
+
// 2. It's not absolute (would mean completely different path on Windows)
|
|
143
|
+
// 3. Empty string means it's the same directory
|
|
144
|
+
return !relativePath.startsWith("..") && !isAbsolute(relativePath);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Check if a path is within the agent's home directory.
|
|
149
|
+
* Normalizes the input path to prevent path traversal attacks.
|
|
150
|
+
* Used by filesystem tools to grant automatic access to agent's own home.
|
|
151
|
+
*/
|
|
152
|
+
export function isWithinAgentHome(inputPath: string, agentPubkey: string): boolean {
|
|
153
|
+
const homeDir = getAgentHomeDirectory(agentPubkey);
|
|
154
|
+
return isPathWithinDirectory(inputPath, homeDir);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Ensure the agent's home directory exists.
|
|
159
|
+
* Creates it if it doesn't exist.
|
|
160
|
+
* @returns true if directory exists or was created, false if creation failed
|
|
161
|
+
*/
|
|
162
|
+
export function ensureAgentHomeDirectory(agentPubkey: string): boolean {
|
|
163
|
+
const homeDir = getAgentHomeDirectory(agentPubkey);
|
|
164
|
+
try {
|
|
165
|
+
mkdirSync(homeDir, { recursive: true });
|
|
166
|
+
return true;
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error("Failed to create agent home dir:", error);
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Maximum file size to read for injected files (prevents memory spikes).
|
|
175
|
+
* We only need first MAX_INJECTED_FILE_LENGTH chars, so read slightly more to detect truncation.
|
|
176
|
+
*/
|
|
177
|
+
const MAX_INJECTED_FILE_READ_SIZE = MAX_INJECTED_FILE_LENGTH + 100;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Read a bounded amount from a file safely, preventing symlink attacks and memory spikes.
|
|
181
|
+
* Uses lstat + realpath validation and bounded reads.
|
|
182
|
+
*
|
|
183
|
+
* @param filePath - Path to the file
|
|
184
|
+
* @param homeDir - The home directory (for containment validation)
|
|
185
|
+
* @param maxBytes - Maximum bytes to read
|
|
186
|
+
* @returns Object with content and whether it was truncated, or null if file should be skipped
|
|
187
|
+
*/
|
|
188
|
+
function safeReadBoundedFile(
|
|
189
|
+
filePath: string,
|
|
190
|
+
homeDir: string,
|
|
191
|
+
maxBytes: number
|
|
192
|
+
): { content: string; truncated: boolean; fileSize: number } | null {
|
|
193
|
+
try {
|
|
194
|
+
// TOCTOU protection: Use lstat (not stat) to check actual file type without following symlinks
|
|
195
|
+
const lstats = lstatSync(filePath);
|
|
196
|
+
|
|
197
|
+
// Skip symlinks entirely (security: prevents symlink race attacks)
|
|
198
|
+
if (lstats.isSymbolicLink()) {
|
|
199
|
+
logger.warn(`Skipping symlink in agent home: ${filePath}`);
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Skip if not a regular file
|
|
204
|
+
if (!lstats.isFile()) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Additional safety: verify realpath is within home directory
|
|
209
|
+
// This catches any edge cases where the file might resolve outside home
|
|
210
|
+
const realPath = realpathSync(filePath);
|
|
211
|
+
const realHomeDir = realpathSync(homeDir);
|
|
212
|
+
const relativePath = relative(realHomeDir, realPath);
|
|
213
|
+
if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
|
|
214
|
+
logger.warn(`Skipping file that resolves outside home: ${filePath} -> ${realPath}`);
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const fileSize = lstats.size;
|
|
219
|
+
|
|
220
|
+
// Bounded read: Only read what we need to prevent memory spikes
|
|
221
|
+
const bytesToRead = Math.min(fileSize, maxBytes);
|
|
222
|
+
const buffer = Buffer.alloc(bytesToRead);
|
|
223
|
+
|
|
224
|
+
// Use low-level open/read for precise control
|
|
225
|
+
const fd = openSync(filePath, fsConstants.O_RDONLY);
|
|
226
|
+
try {
|
|
227
|
+
const bytesRead = readSync(fd, buffer, 0, bytesToRead, 0);
|
|
228
|
+
const content = buffer.slice(0, bytesRead).toString("utf-8");
|
|
229
|
+
const truncated = fileSize > maxBytes;
|
|
230
|
+
|
|
231
|
+
return { content, truncated, fileSize };
|
|
232
|
+
} finally {
|
|
233
|
+
closeSync(fd);
|
|
234
|
+
}
|
|
235
|
+
} catch (error) {
|
|
236
|
+
// File may have been deleted/changed between lstat and read - that's OK
|
|
237
|
+
logger.warn(`Failed to safely read file ${filePath}:`, error);
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Get files in the agent's home directory that start with '+' prefix.
|
|
244
|
+
* These files are auto-injected into the agent's system prompt.
|
|
245
|
+
*
|
|
246
|
+
* Security:
|
|
247
|
+
* - Only reads regular files (skips symlinks and directories)
|
|
248
|
+
* - Uses lstat + realpath validation to prevent TOCTOU/symlink race attacks
|
|
249
|
+
* - Bounded reads to prevent memory spikes from large files
|
|
250
|
+
*
|
|
251
|
+
* Limits: Max 10 files, max 1500 chars per file (truncated if longer).
|
|
252
|
+
*
|
|
253
|
+
* @param agentPubkey - The agent's pubkey
|
|
254
|
+
* @returns Array of injected file objects with filename, content, and truncated flag
|
|
255
|
+
*/
|
|
256
|
+
export function getAgentHomeInjectedFiles(agentPubkey: string): InjectedFile[] {
|
|
257
|
+
const homeDir = getAgentHomeDirectory(agentPubkey);
|
|
258
|
+
|
|
259
|
+
// Ensure directory exists
|
|
260
|
+
if (!ensureAgentHomeDirectory(agentPubkey)) {
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
const entries = readdirSync(homeDir, { withFileTypes: true });
|
|
266
|
+
|
|
267
|
+
// Filter for +prefixed entries that appear to be files
|
|
268
|
+
// Note: We re-validate each file before reading due to TOCTOU concerns
|
|
269
|
+
const plusCandidates = entries
|
|
270
|
+
.filter((entry) => entry.name.startsWith("+") && entry.isFile())
|
|
271
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
272
|
+
.slice(0, MAX_INJECTED_FILES);
|
|
273
|
+
|
|
274
|
+
const injectedFiles: InjectedFile[] = [];
|
|
275
|
+
|
|
276
|
+
for (const entry of plusCandidates) {
|
|
277
|
+
const filePath = join(homeDir, entry.name);
|
|
278
|
+
|
|
279
|
+
// Use safe bounded read with TOCTOU protection
|
|
280
|
+
const result = safeReadBoundedFile(filePath, homeDir, MAX_INJECTED_FILE_READ_SIZE);
|
|
281
|
+
if (!result) {
|
|
282
|
+
continue; // Skip files that couldn't be safely read
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Apply the content length limit
|
|
286
|
+
const truncated = result.content.length > MAX_INJECTED_FILE_LENGTH || result.truncated;
|
|
287
|
+
const content = result.content.slice(0, MAX_INJECTED_FILE_LENGTH);
|
|
288
|
+
|
|
289
|
+
injectedFiles.push({
|
|
290
|
+
filename: entry.name,
|
|
291
|
+
content,
|
|
292
|
+
truncated,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return injectedFiles;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
logger.warn("Failed to scan agent home for injected files:", error);
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Resolve a path that must be within the agent's home directory.
|
|
305
|
+
* Accepts both relative paths (resolved against home) and absolute paths.
|
|
306
|
+
*
|
|
307
|
+
* Security: Uses isPathWithinDirectory() which handles symlinks and path traversal.
|
|
308
|
+
*
|
|
309
|
+
* @param inputPath - Relative or absolute path
|
|
310
|
+
* @param agentPubkey - The agent's pubkey
|
|
311
|
+
* @returns The resolved absolute path within the agent's home
|
|
312
|
+
* @throws HomeScopeViolationError if path escapes home directory
|
|
313
|
+
*/
|
|
314
|
+
export function resolveHomeScopedPath(inputPath: string, agentPubkey: string): string {
|
|
315
|
+
const homeDir = getAgentHomeDirectory(agentPubkey);
|
|
316
|
+
|
|
317
|
+
// Ensure home directory exists
|
|
318
|
+
ensureAgentHomeDirectory(agentPubkey);
|
|
319
|
+
|
|
320
|
+
// Resolve path: treat relative paths as relative to home directory
|
|
321
|
+
const resolvedPath = isAbsolute(inputPath)
|
|
322
|
+
? inputPath
|
|
323
|
+
: join(homeDir, inputPath);
|
|
324
|
+
|
|
325
|
+
// Validate the resolved path is within home directory
|
|
326
|
+
if (!isPathWithinDirectory(resolvedPath, homeDir)) {
|
|
327
|
+
throw new HomeScopeViolationError(
|
|
328
|
+
`Path "${inputPath}" is outside your home directory. ` +
|
|
329
|
+
`You can only access files within your home directory.`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return resolvedPath;
|
|
334
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// ToolError removed - define it locally if needed
|
|
2
|
+
interface ToolError {
|
|
3
|
+
kind: "validation" | "execution" | "system";
|
|
4
|
+
message: string;
|
|
5
|
+
field?: string;
|
|
6
|
+
tool?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Comprehensive error formatter that handles all error types in the codebase
|
|
11
|
+
* Consolidates error formatting logic from various parts of the system
|
|
12
|
+
*/
|
|
13
|
+
export function formatAnyError(error: unknown): string {
|
|
14
|
+
// Handle null/undefined
|
|
15
|
+
if (error == null) {
|
|
16
|
+
return "Unknown error";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Handle strings
|
|
20
|
+
if (typeof error === "string") {
|
|
21
|
+
return error;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Handle Error instances
|
|
25
|
+
if (error instanceof Error) {
|
|
26
|
+
return error.message;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Handle objects
|
|
30
|
+
if (typeof error === "object") {
|
|
31
|
+
const errorObj = error as Record<string, unknown>;
|
|
32
|
+
|
|
33
|
+
// Check for ToolError structure (with type guard)
|
|
34
|
+
if ("kind" in errorObj && "message" in errorObj) {
|
|
35
|
+
const kind = errorObj.kind;
|
|
36
|
+
if (kind === "validation" || kind === "execution" || kind === "system") {
|
|
37
|
+
return formatToolError(errorObj as unknown as ToolError);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check for simple message property
|
|
42
|
+
if ("message" in errorObj && typeof errorObj.message === "string") {
|
|
43
|
+
return errorObj.message;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Try to extract meaningful properties from the error object
|
|
47
|
+
const parts: string[] = [];
|
|
48
|
+
|
|
49
|
+
// Common error properties
|
|
50
|
+
if ("kind" in errorObj) parts.push(`kind: ${errorObj.kind}`);
|
|
51
|
+
if ("field" in errorObj) parts.push(`field: ${errorObj.field}`);
|
|
52
|
+
if ("tool" in errorObj) parts.push(`tool: ${errorObj.tool}`);
|
|
53
|
+
if ("code" in errorObj) parts.push(`code: ${errorObj.code}`);
|
|
54
|
+
if ("statusCode" in errorObj) parts.push(`statusCode: ${errorObj.statusCode}`);
|
|
55
|
+
if ("errno" in errorObj) parts.push(`errno: ${errorObj.errno}`);
|
|
56
|
+
if ("syscall" in errorObj) parts.push(`syscall: ${errorObj.syscall}`);
|
|
57
|
+
|
|
58
|
+
// If we found specific properties, use them
|
|
59
|
+
if (parts.length > 0) {
|
|
60
|
+
return parts.join(", ");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Otherwise, try to stringify the object
|
|
64
|
+
try {
|
|
65
|
+
const str = JSON.stringify(error);
|
|
66
|
+
// Don't return huge JSON strings
|
|
67
|
+
if (str.length > 200) {
|
|
68
|
+
return "[Complex Error Object]";
|
|
69
|
+
}
|
|
70
|
+
return str;
|
|
71
|
+
} catch {
|
|
72
|
+
return "[Complex Error Object]";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Fallback to String conversion
|
|
77
|
+
return String(error);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Format ToolError objects into human-readable strings
|
|
82
|
+
*/
|
|
83
|
+
export function formatToolError(error: ToolError): string {
|
|
84
|
+
switch (error.kind) {
|
|
85
|
+
case "validation":
|
|
86
|
+
// If the field is empty and message is just "Required", make it clearer
|
|
87
|
+
if (error.field === "" && error.message === "Required") {
|
|
88
|
+
return "Validation error: Missing required parameter";
|
|
89
|
+
}
|
|
90
|
+
return error.field
|
|
91
|
+
? `Validation error in ${error.field}: ${error.message}`
|
|
92
|
+
: `Validation error: ${error.message}`;
|
|
93
|
+
case "execution":
|
|
94
|
+
return error.tool
|
|
95
|
+
? `Execution error in ${error.tool}: ${error.message}`
|
|
96
|
+
: `Execution error: ${error.message}`;
|
|
97
|
+
case "system":
|
|
98
|
+
return `System error: ${error.message}`;
|
|
99
|
+
default: {
|
|
100
|
+
// This should never happen with proper ToolError types
|
|
101
|
+
const unknownError = error as unknown as Record<string, unknown>;
|
|
102
|
+
return (
|
|
103
|
+
(typeof unknownError.message === "string" ? unknownError.message : null) ||
|
|
104
|
+
"Unknown error"
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Constants for AI error markers
|
|
111
|
+
const AI_API_CALL_ERROR = "AI_APICallError";
|
|
112
|
+
const PROVIDER_RETURNED_ERROR = "Provider returned error";
|
|
113
|
+
const OPENROUTER_MARKER = "openrouter";
|
|
114
|
+
const HTTP_422_STATUS = "422";
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Generic prefixes that indicate the message itself is not meaningful
|
|
118
|
+
* and we should try to extract details from toString() instead.
|
|
119
|
+
* These will be checked with startsWith, so "AI_APICallError" will match
|
|
120
|
+
* "AI_APICallError: some details" etc.
|
|
121
|
+
*/
|
|
122
|
+
const GENERIC_ERROR_PREFIXES = [
|
|
123
|
+
AI_API_CALL_ERROR,
|
|
124
|
+
PROVIDER_RETURNED_ERROR,
|
|
125
|
+
HTTP_422_STATUS,
|
|
126
|
+
"Unprocessable Entity",
|
|
127
|
+
"Error:",
|
|
128
|
+
] as const;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if an error message is meaningful enough to use directly,
|
|
132
|
+
* or if it's a generic wrapper that requires regex extraction for details.
|
|
133
|
+
*
|
|
134
|
+
* A message is considered "meaningful" if:
|
|
135
|
+
* 1. It's not empty
|
|
136
|
+
* 2. It doesn't start with any known generic prefix
|
|
137
|
+
* 3. It's not just the error class name
|
|
138
|
+
*/
|
|
139
|
+
export function isMeaningfulAiMessage(message: string | undefined): boolean {
|
|
140
|
+
if (!message || message.trim() === "") {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const trimmedMessage = message.trim();
|
|
145
|
+
|
|
146
|
+
// Check if message starts with any generic prefix
|
|
147
|
+
for (const prefix of GENERIC_ERROR_PREFIXES) {
|
|
148
|
+
if (trimmedMessage.startsWith(prefix)) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check for HTTP status code patterns (e.g., "422", "500 Internal Server Error")
|
|
154
|
+
if (/^\d{3}\b/.test(trimmedMessage)) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Format error for stream/execution errors from LLM providers
|
|
163
|
+
*/
|
|
164
|
+
export function formatStreamError(error: unknown): { message: string; errorType: string } {
|
|
165
|
+
let errorMessage = "An error occurred while processing your request.";
|
|
166
|
+
let errorType = "system";
|
|
167
|
+
|
|
168
|
+
if (error instanceof Error) {
|
|
169
|
+
const errorStr = error.toString();
|
|
170
|
+
if (
|
|
171
|
+
errorStr.includes(AI_API_CALL_ERROR) ||
|
|
172
|
+
errorStr.includes(PROVIDER_RETURNED_ERROR) ||
|
|
173
|
+
errorStr.includes(HTTP_422_STATUS) ||
|
|
174
|
+
errorStr.includes(OPENROUTER_MARKER)
|
|
175
|
+
) {
|
|
176
|
+
errorType = "ai_api";
|
|
177
|
+
|
|
178
|
+
// Check if error.message is meaningful (not a generic wrapper)
|
|
179
|
+
// Claude Code errors often have the real error in error.message
|
|
180
|
+
if (isMeaningfulAiMessage(error.message)) {
|
|
181
|
+
errorMessage = `AI Error: ${error.message}`;
|
|
182
|
+
} else {
|
|
183
|
+
// Fall back to regex extraction for OpenRouter-style errors
|
|
184
|
+
const providerMatch = errorStr.match(/provider_name":"([^"]+)"/);
|
|
185
|
+
const provider = providerMatch ? providerMatch[1] : "AI provider";
|
|
186
|
+
errorMessage = `Failed to process request with ${provider}. The AI service returned an error.`;
|
|
187
|
+
|
|
188
|
+
// Add raw error details if available
|
|
189
|
+
const rawMatch = errorStr.match(/raw":"([^"]+)"/);
|
|
190
|
+
if (rawMatch) {
|
|
191
|
+
errorMessage += ` Details: ${rawMatch[1]}`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
errorMessage = `Error: ${error.message}`;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return { message: errorMessage, errorType };
|
|
200
|
+
}
|