@xenosystem/agent-interface-host 0.1.3
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/LICENSE +67 -0
- package/dist/acp/AcpAgentRegistryService.d.ts +77 -0
- package/dist/acp/AcpAgentRegistryService.d.ts.map +1 -0
- package/dist/acp/AcpAgentRegistryService.js +754 -0
- package/dist/acp/AcpAgentRegistryService.js.map +1 -0
- package/dist/acp/AcpAgentTurnExecutionAdapter.d.ts +56 -0
- package/dist/acp/AcpAgentTurnExecutionAdapter.d.ts.map +1 -0
- package/dist/acp/AcpAgentTurnExecutionAdapter.js +323 -0
- package/dist/acp/AcpAgentTurnExecutionAdapter.js.map +1 -0
- package/dist/acp/AcpApprovalPortAdapter.d.ts +41 -0
- package/dist/acp/AcpApprovalPortAdapter.d.ts.map +1 -0
- package/dist/acp/AcpApprovalPortAdapter.js +82 -0
- package/dist/acp/AcpApprovalPortAdapter.js.map +1 -0
- package/dist/acp/AcpCoordinationBridge.d.ts +13 -0
- package/dist/acp/AcpCoordinationBridge.d.ts.map +1 -0
- package/dist/acp/AcpCoordinationBridge.js +13 -0
- package/dist/acp/AcpCoordinationBridge.js.map +1 -0
- package/dist/acp/AcpDiagnosticsService.d.ts +61 -0
- package/dist/acp/AcpDiagnosticsService.d.ts.map +1 -0
- package/dist/acp/AcpDiagnosticsService.js +203 -0
- package/dist/acp/AcpDiagnosticsService.js.map +1 -0
- package/dist/acp/AcpExternalCertificationHarness.d.ts +47 -0
- package/dist/acp/AcpExternalCertificationHarness.d.ts.map +1 -0
- package/dist/acp/AcpExternalCertificationHarness.js +176 -0
- package/dist/acp/AcpExternalCertificationHarness.js.map +1 -0
- package/dist/acp/AcpHostBridge.d.ts +81 -0
- package/dist/acp/AcpHostBridge.d.ts.map +1 -0
- package/dist/acp/AcpHostBridge.js +101 -0
- package/dist/acp/AcpHostBridge.js.map +1 -0
- package/dist/acp/AcpInteractivePermissionBridge.d.ts +77 -0
- package/dist/acp/AcpInteractivePermissionBridge.d.ts.map +1 -0
- package/dist/acp/AcpInteractivePermissionBridge.js +127 -0
- package/dist/acp/AcpInteractivePermissionBridge.js.map +1 -0
- package/dist/acp/AcpPermissionBridge.d.ts +19 -0
- package/dist/acp/AcpPermissionBridge.d.ts.map +1 -0
- package/dist/acp/AcpPermissionBridge.js +110 -0
- package/dist/acp/AcpPermissionBridge.js.map +1 -0
- package/dist/acp/AcpPermissionPolicy.d.ts +111 -0
- package/dist/acp/AcpPermissionPolicy.d.ts.map +1 -0
- package/dist/acp/AcpPermissionPolicy.js +167 -0
- package/dist/acp/AcpPermissionPolicy.js.map +1 -0
- package/dist/acp/AcpProcessSupervisor.d.ts +134 -0
- package/dist/acp/AcpProcessSupervisor.d.ts.map +1 -0
- package/dist/acp/AcpProcessSupervisor.js +674 -0
- package/dist/acp/AcpProcessSupervisor.js.map +1 -0
- package/dist/acp/AcpProviderApprovalService.d.ts +27 -0
- package/dist/acp/AcpProviderApprovalService.d.ts.map +1 -0
- package/dist/acp/AcpProviderApprovalService.js +128 -0
- package/dist/acp/AcpProviderApprovalService.js.map +1 -0
- package/dist/acp/AcpProviderCatalogService.d.ts +69 -0
- package/dist/acp/AcpProviderCatalogService.d.ts.map +1 -0
- package/dist/acp/AcpProviderCatalogService.js +707 -0
- package/dist/acp/AcpProviderCatalogService.js.map +1 -0
- package/dist/acp/AcpProviderModelService.d.ts +6 -0
- package/dist/acp/AcpProviderModelService.d.ts.map +1 -0
- package/dist/acp/AcpProviderModelService.js +274 -0
- package/dist/acp/AcpProviderModelService.js.map +1 -0
- package/dist/acp/AcpProviderVersionReadiness.d.ts +6 -0
- package/dist/acp/AcpProviderVersionReadiness.d.ts.map +1 -0
- package/dist/acp/AcpProviderVersionReadiness.js +100 -0
- package/dist/acp/AcpProviderVersionReadiness.js.map +1 -0
- package/dist/acp/AcpRuntimeEventMapper.d.ts +16 -0
- package/dist/acp/AcpRuntimeEventMapper.d.ts.map +1 -0
- package/dist/acp/AcpRuntimeEventMapper.js +326 -0
- package/dist/acp/AcpRuntimeEventMapper.js.map +1 -0
- package/dist/acp/AcpSessionService.d.ts +65 -0
- package/dist/acp/AcpSessionService.d.ts.map +1 -0
- package/dist/acp/AcpSessionService.js +931 -0
- package/dist/acp/AcpSessionService.js.map +1 -0
- package/dist/acp/DurableAcpPermissionPolicyStore.d.ts +25 -0
- package/dist/acp/DurableAcpPermissionPolicyStore.d.ts.map +1 -0
- package/dist/acp/DurableAcpPermissionPolicyStore.js +100 -0
- package/dist/acp/DurableAcpPermissionPolicyStore.js.map +1 -0
- package/dist/acp/acpCommandSafety.d.ts +63 -0
- package/dist/acp/acpCommandSafety.d.ts.map +1 -0
- package/dist/acp/acpCommandSafety.js +90 -0
- package/dist/acp/acpCommandSafety.js.map +1 -0
- package/dist/acp/acpErrors.d.ts +47 -0
- package/dist/acp/acpErrors.d.ts.map +1 -0
- package/dist/acp/acpErrors.js +93 -0
- package/dist/acp/acpErrors.js.map +1 -0
- package/dist/acp/acpRedaction.d.ts +9 -0
- package/dist/acp/acpRedaction.d.ts.map +1 -0
- package/dist/acp/acpRedaction.js +69 -0
- package/dist/acp/acpRedaction.js.map +1 -0
- package/dist/acp/acpSessionResume.d.ts +8 -0
- package/dist/acp/acpSessionResume.d.ts.map +1 -0
- package/dist/acp/acpSessionResume.js +31 -0
- package/dist/acp/acpSessionResume.js.map +1 -0
- package/dist/acp/acpTypes.d.ts +467 -0
- package/dist/acp/acpTypes.d.ts.map +1 -0
- package/dist/acp/acpTypes.js +2 -0
- package/dist/acp/acpTypes.js.map +1 -0
- package/dist/agent/chooseDirectoryBridge.d.ts +29 -0
- package/dist/agent/chooseDirectoryBridge.d.ts.map +1 -0
- package/dist/agent/chooseDirectoryBridge.js +116 -0
- package/dist/agent/chooseDirectoryBridge.js.map +1 -0
- package/dist/agent/permissionProfiles.d.ts +29 -0
- package/dist/agent/permissionProfiles.d.ts.map +1 -0
- package/dist/agent/permissionProfiles.js +107 -0
- package/dist/agent/permissionProfiles.js.map +1 -0
- package/dist/agent/runtimeAuthorityPrompt.d.ts +13 -0
- package/dist/agent/runtimeAuthorityPrompt.d.ts.map +1 -0
- package/dist/agent/runtimeAuthorityPrompt.js +45 -0
- package/dist/agent/runtimeAuthorityPrompt.js.map +1 -0
- package/dist/agent/runtimeModeOutputGuard.d.ts +6 -0
- package/dist/agent/runtimeModeOutputGuard.d.ts.map +1 -0
- package/dist/agent/runtimeModeOutputGuard.js +40 -0
- package/dist/agent/runtimeModeOutputGuard.js.map +1 -0
- package/dist/agent/workspacePlacementBridge.d.ts +29 -0
- package/dist/agent/workspacePlacementBridge.d.ts.map +1 -0
- package/dist/agent/workspacePlacementBridge.js +143 -0
- package/dist/agent/workspacePlacementBridge.js.map +1 -0
- package/dist/agentSearch/AgentSearchService.d.ts +81 -0
- package/dist/agentSearch/AgentSearchService.d.ts.map +1 -0
- package/dist/agentSearch/AgentSearchService.js +1038 -0
- package/dist/agentSearch/AgentSearchService.js.map +1 -0
- package/dist/agentSearch/JsonInvertedIndexAdapter.d.ts +44 -0
- package/dist/agentSearch/JsonInvertedIndexAdapter.d.ts.map +1 -0
- package/dist/agentSearch/JsonInvertedIndexAdapter.js +307 -0
- package/dist/agentSearch/JsonInvertedIndexAdapter.js.map +1 -0
- package/dist/agentSearch/SearchIndexAdapter.d.ts +33 -0
- package/dist/agentSearch/SearchIndexAdapter.d.ts.map +1 -0
- package/dist/agentSearch/SearchIndexAdapter.js +2 -0
- package/dist/agentSearch/SearchIndexAdapter.js.map +1 -0
- package/dist/agentSearch/SearchSourceWatcher.d.ts +29 -0
- package/dist/agentSearch/SearchSourceWatcher.d.ts.map +1 -0
- package/dist/agentSearch/SearchSourceWatcher.js +150 -0
- package/dist/agentSearch/SearchSourceWatcher.js.map +1 -0
- package/dist/agentSearch/SqliteFtsIndexAdapter.d.ts +58 -0
- package/dist/agentSearch/SqliteFtsIndexAdapter.d.ts.map +1 -0
- package/dist/agentSearch/SqliteFtsIndexAdapter.js +420 -0
- package/dist/agentSearch/SqliteFtsIndexAdapter.js.map +1 -0
- package/dist/agentSearch/conversationIndexer.d.ts +69 -0
- package/dist/agentSearch/conversationIndexer.d.ts.map +1 -0
- package/dist/agentSearch/conversationIndexer.js +313 -0
- package/dist/agentSearch/conversationIndexer.js.map +1 -0
- package/dist/agentSearch/searchTypes.d.ts +264 -0
- package/dist/agentSearch/searchTypes.d.ts.map +1 -0
- package/dist/agentSearch/searchTypes.js +2 -0
- package/dist/agentSearch/searchTypes.js.map +1 -0
- package/dist/agentSearch/searchUtils.d.ts +12 -0
- package/dist/agentSearch/searchUtils.d.ts.map +1 -0
- package/dist/agentSearch/searchUtils.js +125 -0
- package/dist/agentSearch/searchUtils.js.map +1 -0
- package/dist/agentSearch/workspaceMetadataIndexer.d.ts +3 -0
- package/dist/agentSearch/workspaceMetadataIndexer.d.ts.map +1 -0
- package/dist/agentSearch/workspaceMetadataIndexer.js +125 -0
- package/dist/agentSearch/workspaceMetadataIndexer.js.map +1 -0
- package/dist/cli/CliControlPlaneService.d.ts +269 -0
- package/dist/cli/CliControlPlaneService.d.ts.map +1 -0
- package/dist/cli/CliControlPlaneService.js +1874 -0
- package/dist/cli/CliControlPlaneService.js.map +1 -0
- package/dist/cloud/XenoCloudAgentTurnExecutionAdapter.d.ts +91 -0
- package/dist/cloud/XenoCloudAgentTurnExecutionAdapter.d.ts.map +1 -0
- package/dist/cloud/XenoCloudAgentTurnExecutionAdapter.js +379 -0
- package/dist/cloud/XenoCloudAgentTurnExecutionAdapter.js.map +1 -0
- package/dist/codebase/CodebaseIntelligenceService.d.ts +68 -0
- package/dist/codebase/CodebaseIntelligenceService.d.ts.map +1 -0
- package/dist/codebase/CodebaseIntelligenceService.js +1717 -0
- package/dist/codebase/CodebaseIntelligenceService.js.map +1 -0
- package/dist/codebase/LanguageServerProcessManager.d.ts +85 -0
- package/dist/codebase/LanguageServerProcessManager.d.ts.map +1 -0
- package/dist/codebase/LanguageServerProcessManager.js +607 -0
- package/dist/codebase/LanguageServerProcessManager.js.map +1 -0
- package/dist/codebase/codeTypes.d.ts +485 -0
- package/dist/codebase/codeTypes.d.ts.map +1 -0
- package/dist/codebase/codeTypes.js +2 -0
- package/dist/codebase/codeTypes.js.map +1 -0
- package/dist/codebase/codeUtils.d.ts +24 -0
- package/dist/codebase/codeUtils.d.ts.map +1 -0
- package/dist/codebase/codeUtils.js +177 -0
- package/dist/codebase/codeUtils.js.map +1 -0
- package/dist/codebase/contextPacks.d.ts +18 -0
- package/dist/codebase/contextPacks.d.ts.map +1 -0
- package/dist/codebase/contextPacks.js +180 -0
- package/dist/codebase/contextPacks.js.map +1 -0
- package/dist/codebase/dependencyGraph.d.ts +3 -0
- package/dist/codebase/dependencyGraph.d.ts.map +1 -0
- package/dist/codebase/dependencyGraph.js +209 -0
- package/dist/codebase/dependencyGraph.js.map +1 -0
- package/dist/codebase/fileIndexer.d.ts +27 -0
- package/dist/codebase/fileIndexer.d.ts.map +1 -0
- package/dist/codebase/fileIndexer.js +167 -0
- package/dist/codebase/fileIndexer.js.map +1 -0
- package/dist/codebase/symbolIndexer.d.ts +3 -0
- package/dist/codebase/symbolIndexer.d.ts.map +1 -0
- package/dist/codebase/symbolIndexer.js +184 -0
- package/dist/codebase/symbolIndexer.js.map +1 -0
- package/dist/codebase/whatChanged.d.ts +4 -0
- package/dist/codebase/whatChanged.d.ts.map +1 -0
- package/dist/codebase/whatChanged.js +138 -0
- package/dist/codebase/whatChanged.js.map +1 -0
- package/dist/coordination/CoordinationService.d.ts +166 -0
- package/dist/coordination/CoordinationService.d.ts.map +1 -0
- package/dist/coordination/CoordinationService.js +247 -0
- package/dist/coordination/CoordinationService.js.map +1 -0
- package/dist/coordination/claims.d.ts +122 -0
- package/dist/coordination/claims.d.ts.map +1 -0
- package/dist/coordination/claims.js +196 -0
- package/dist/coordination/claims.js.map +1 -0
- package/dist/coordination/coordinationTools.d.ts +40 -0
- package/dist/coordination/coordinationTools.d.ts.map +1 -0
- package/dist/coordination/coordinationTools.js +92 -0
- package/dist/coordination/coordinationTools.js.map +1 -0
- package/dist/coordination/laneBridge.d.ts +84 -0
- package/dist/coordination/laneBridge.d.ts.map +1 -0
- package/dist/coordination/laneBridge.js +86 -0
- package/dist/coordination/laneBridge.js.map +1 -0
- package/dist/coordination/messages.d.ts +225 -0
- package/dist/coordination/messages.d.ts.map +1 -0
- package/dist/coordination/messages.js +335 -0
- package/dist/coordination/messages.js.map +1 -0
- package/dist/coordination/permissionWaitRegistry.d.ts +32 -0
- package/dist/coordination/permissionWaitRegistry.d.ts.map +1 -0
- package/dist/coordination/permissionWaitRegistry.js +59 -0
- package/dist/coordination/permissionWaitRegistry.js.map +1 -0
- package/dist/coordination/query.d.ts +187 -0
- package/dist/coordination/query.d.ts.map +1 -0
- package/dist/coordination/query.js +138 -0
- package/dist/coordination/query.js.map +1 -0
- package/dist/coordination/renderInbox.d.ts +15 -0
- package/dist/coordination/renderInbox.d.ts.map +1 -0
- package/dist/coordination/renderInbox.js +19 -0
- package/dist/coordination/renderInbox.js.map +1 -0
- package/dist/coordination/runtimeEvents.d.ts +53 -0
- package/dist/coordination/runtimeEvents.d.ts.map +1 -0
- package/dist/coordination/runtimeEvents.js +112 -0
- package/dist/coordination/runtimeEvents.js.map +1 -0
- package/dist/detachedHostService.d.ts +77 -0
- package/dist/detachedHostService.d.ts.map +1 -0
- package/dist/detachedHostService.js +66 -0
- package/dist/detachedHostService.js.map +1 -0
- package/dist/detachedHostSupervisor.d.ts +102 -0
- package/dist/detachedHostSupervisor.d.ts.map +1 -0
- package/dist/detachedHostSupervisor.js +168 -0
- package/dist/detachedHostSupervisor.js.map +1 -0
- package/dist/durableRuntimeEventStore.d.ts +49 -0
- package/dist/durableRuntimeEventStore.d.ts.map +1 -0
- package/dist/durableRuntimeEventStore.js +364 -0
- package/dist/durableRuntimeEventStore.js.map +1 -0
- package/dist/engineering/EngineeringWorkbenchService.d.ts +142 -0
- package/dist/engineering/EngineeringWorkbenchService.d.ts.map +1 -0
- package/dist/engineering/EngineeringWorkbenchService.js +2301 -0
- package/dist/engineering/EngineeringWorkbenchService.js.map +1 -0
- package/dist/engineering/LandingService.d.ts +112 -0
- package/dist/engineering/LandingService.d.ts.map +1 -0
- package/dist/engineering/LandingService.js +239 -0
- package/dist/engineering/LandingService.js.map +1 -0
- package/dist/engineering/engineeringTypes.d.ts +2 -0
- package/dist/engineering/engineeringTypes.d.ts.map +1 -0
- package/dist/engineering/engineeringTypes.js +2 -0
- package/dist/engineering/engineeringTypes.js.map +1 -0
- package/dist/engineering/patchReview.d.ts +11 -0
- package/dist/engineering/patchReview.d.ts.map +1 -0
- package/dist/engineering/patchReview.js +174 -0
- package/dist/engineering/patchReview.js.map +1 -0
- package/dist/index.d.ts +834 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3242 -0
- package/dist/index.js.map +1 -0
- package/dist/intake/IntakeService.d.ts +84 -0
- package/dist/intake/IntakeService.d.ts.map +1 -0
- package/dist/intake/IntakeService.js +117 -0
- package/dist/intake/IntakeService.js.map +1 -0
- package/dist/intake/RepositoryTodoIntakeSource.d.ts +72 -0
- package/dist/intake/RepositoryTodoIntakeSource.d.ts.map +1 -0
- package/dist/intake/RepositoryTodoIntakeSource.js +168 -0
- package/dist/intake/RepositoryTodoIntakeSource.js.map +1 -0
- package/dist/intake/WorkspaceTodoIntakeSource.d.ts +53 -0
- package/dist/intake/WorkspaceTodoIntakeSource.d.ts.map +1 -0
- package/dist/intake/WorkspaceTodoIntakeSource.js +81 -0
- package/dist/intake/WorkspaceTodoIntakeSource.js.map +1 -0
- package/dist/isolation/CommandExecution.d.ts +132 -0
- package/dist/isolation/CommandExecution.d.ts.map +1 -0
- package/dist/isolation/CommandExecution.js +48 -0
- package/dist/isolation/CommandExecution.js.map +1 -0
- package/dist/isolation/LocalCommandExecutor.d.ts +27 -0
- package/dist/isolation/LocalCommandExecutor.d.ts.map +1 -0
- package/dist/isolation/LocalCommandExecutor.js +97 -0
- package/dist/isolation/LocalCommandExecutor.js.map +1 -0
- package/dist/isolation/RunExecutionEnvironment.d.ts +135 -0
- package/dist/isolation/RunExecutionEnvironment.d.ts.map +1 -0
- package/dist/isolation/RunExecutionEnvironment.js +206 -0
- package/dist/isolation/RunExecutionEnvironment.js.map +1 -0
- package/dist/isolation/RunIsolationService.d.ts +90 -0
- package/dist/isolation/RunIsolationService.d.ts.map +1 -0
- package/dist/isolation/RunIsolationService.js +86 -0
- package/dist/isolation/RunIsolationService.js.map +1 -0
- package/dist/isolation/SandboxCommandExecutor.d.ts +93 -0
- package/dist/isolation/SandboxCommandExecutor.d.ts.map +1 -0
- package/dist/isolation/SandboxCommandExecutor.js +189 -0
- package/dist/isolation/SandboxCommandExecutor.js.map +1 -0
- package/dist/knowledge/WorkspaceKnowledgeService.d.ts +71 -0
- package/dist/knowledge/WorkspaceKnowledgeService.d.ts.map +1 -0
- package/dist/knowledge/WorkspaceKnowledgeService.js +103 -0
- package/dist/knowledge/WorkspaceKnowledgeService.js.map +1 -0
- package/dist/localHostAuthority.d.ts +24 -0
- package/dist/localHostAuthority.d.ts.map +1 -0
- package/dist/localHostAuthority.js +87 -0
- package/dist/localHostAuthority.js.map +1 -0
- package/dist/localHostLease.d.ts +51 -0
- package/dist/localHostLease.d.ts.map +1 -0
- package/dist/localHostLease.js +167 -0
- package/dist/localHostLease.js.map +1 -0
- package/dist/localHostPaths.d.ts +30 -0
- package/dist/localHostPaths.d.ts.map +1 -0
- package/dist/localHostPaths.js +60 -0
- package/dist/localHostPaths.js.map +1 -0
- package/dist/localRpcTransport.d.ts +21 -0
- package/dist/localRpcTransport.d.ts.map +1 -0
- package/dist/localRpcTransport.js +423 -0
- package/dist/localRpcTransport.js.map +1 -0
- package/dist/node.d.ts +104 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +103 -0
- package/dist/node.js.map +1 -0
- package/dist/observation/DirectoryBaselineScanner.d.ts +44 -0
- package/dist/observation/DirectoryBaselineScanner.d.ts.map +1 -0
- package/dist/observation/DirectoryBaselineScanner.js +112 -0
- package/dist/observation/DirectoryBaselineScanner.js.map +1 -0
- package/dist/observation/MountContentReader.d.ts +63 -0
- package/dist/observation/MountContentReader.d.ts.map +1 -0
- package/dist/observation/MountContentReader.js +158 -0
- package/dist/observation/MountContentReader.js.map +1 -0
- package/dist/observation/RunObservationService.d.ts +229 -0
- package/dist/observation/RunObservationService.d.ts.map +1 -0
- package/dist/observation/RunObservationService.js +248 -0
- package/dist/observation/RunObservationService.js.map +1 -0
- package/dist/observation/XenoUseObservationSource.d.ts +42 -0
- package/dist/observation/XenoUseObservationSource.d.ts.map +1 -0
- package/dist/observation/XenoUseObservationSource.js +104 -0
- package/dist/observation/XenoUseObservationSource.js.map +1 -0
- package/dist/observation/XenoUseRunObserver.d.ts +140 -0
- package/dist/observation/XenoUseRunObserver.d.ts.map +1 -0
- package/dist/observation/XenoUseRunObserver.js +256 -0
- package/dist/observation/XenoUseRunObserver.js.map +1 -0
- package/dist/pluginMcp/PluginMcpManagerService.d.ts +141 -0
- package/dist/pluginMcp/PluginMcpManagerService.d.ts.map +1 -0
- package/dist/pluginMcp/PluginMcpManagerService.js +1821 -0
- package/dist/pluginMcp/PluginMcpManagerService.js.map +1 -0
- package/dist/pluginMcp/pluginMcpTypes.d.ts +349 -0
- package/dist/pluginMcp/pluginMcpTypes.d.ts.map +1 -0
- package/dist/pluginMcp/pluginMcpTypes.js +2 -0
- package/dist/pluginMcp/pluginMcpTypes.js.map +1 -0
- package/dist/policy/WorkspacePolicyService.d.ts +83 -0
- package/dist/policy/WorkspacePolicyService.d.ts.map +1 -0
- package/dist/policy/WorkspacePolicyService.js +809 -0
- package/dist/policy/WorkspacePolicyService.js.map +1 -0
- package/dist/policy/policyGuard.d.ts +27 -0
- package/dist/policy/policyGuard.d.ts.map +1 -0
- package/dist/policy/policyGuard.js +143 -0
- package/dist/policy/policyGuard.js.map +1 -0
- package/dist/policy/policyTypes.d.ts +190 -0
- package/dist/policy/policyTypes.d.ts.map +1 -0
- package/dist/policy/policyTypes.js +2 -0
- package/dist/policy/policyTypes.js.map +1 -0
- package/dist/runtime/RuntimeEventStore.d.ts +42 -0
- package/dist/runtime/RuntimeEventStore.d.ts.map +1 -0
- package/dist/runtime/RuntimeEventStore.js +412 -0
- package/dist/runtime/RuntimeEventStore.js.map +1 -0
- package/dist/runtime/RuntimeEventStoreRepositoryAdapter.d.ts +21 -0
- package/dist/runtime/RuntimeEventStoreRepositoryAdapter.d.ts.map +1 -0
- package/dist/runtime/RuntimeEventStoreRepositoryAdapter.js +41 -0
- package/dist/runtime/RuntimeEventStoreRepositoryAdapter.js.map +1 -0
- package/dist/runtime/RuntimeTimelineProjection.d.ts +5 -0
- package/dist/runtime/RuntimeTimelineProjection.d.ts.map +1 -0
- package/dist/runtime/RuntimeTimelineProjection.js +705 -0
- package/dist/runtime/RuntimeTimelineProjection.js.map +1 -0
- package/dist/runtime/runtimeEventTypes.d.ts +73 -0
- package/dist/runtime/runtimeEventTypes.d.ts.map +1 -0
- package/dist/runtime/runtimeEventTypes.js +2 -0
- package/dist/runtime/runtimeEventTypes.js.map +1 -0
- package/dist/sdk/SdkNativeAgentTurnExecutionAdapter.d.ts +117 -0
- package/dist/sdk/SdkNativeAgentTurnExecutionAdapter.d.ts.map +1 -0
- package/dist/sdk/SdkNativeAgentTurnExecutionAdapter.js +214 -0
- package/dist/sdk/SdkNativeAgentTurnExecutionAdapter.js.map +1 -0
- package/dist/session/AgentSessionService.d.ts +285 -0
- package/dist/session/AgentSessionService.d.ts.map +1 -0
- package/dist/session/AgentSessionService.js +444 -0
- package/dist/session/AgentSessionService.js.map +1 -0
- package/dist/session/bufferedSessionPty.d.ts +27 -0
- package/dist/session/bufferedSessionPty.d.ts.map +1 -0
- package/dist/session/bufferedSessionPty.js +49 -0
- package/dist/session/bufferedSessionPty.js.map +1 -0
- package/dist/session/renderSupport.d.ts +53 -0
- package/dist/session/renderSupport.d.ts.map +1 -0
- package/dist/session/renderSupport.js +41 -0
- package/dist/session/renderSupport.js.map +1 -0
- package/dist/session/runtimeEventChannel.d.ts +56 -0
- package/dist/session/runtimeEventChannel.d.ts.map +1 -0
- package/dist/session/runtimeEventChannel.js +53 -0
- package/dist/session/runtimeEventChannel.js.map +1 -0
- package/dist/state/JsonAgentStateRepository.d.ts +29 -0
- package/dist/state/JsonAgentStateRepository.d.ts.map +1 -0
- package/dist/state/JsonAgentStateRepository.js +165 -0
- package/dist/state/JsonAgentStateRepository.js.map +1 -0
- package/dist/state/SqliteAgentStateRepository.d.ts +58 -0
- package/dist/state/SqliteAgentStateRepository.d.ts.map +1 -0
- package/dist/state/SqliteAgentStateRepository.js +777 -0
- package/dist/state/SqliteAgentStateRepository.js.map +1 -0
- package/dist/state/createAgentStateRepository.d.ts +4 -0
- package/dist/state/createAgentStateRepository.d.ts.map +1 -0
- package/dist/state/createAgentStateRepository.js +12 -0
- package/dist/state/createAgentStateRepository.js.map +1 -0
- package/dist/state/stateSanitizers.d.ts +7 -0
- package/dist/state/stateSanitizers.d.ts.map +1 -0
- package/dist/state/stateSanitizers.js +87 -0
- package/dist/state/stateSanitizers.js.map +1 -0
- package/dist/subagents/AcpSubagentExecutionAdapter.d.ts +12 -0
- package/dist/subagents/AcpSubagentExecutionAdapter.d.ts.map +1 -0
- package/dist/subagents/AcpSubagentExecutionAdapter.js +119 -0
- package/dist/subagents/AcpSubagentExecutionAdapter.js.map +1 -0
- package/dist/subagents/CliSubagentExecutionAdapter.d.ts +13 -0
- package/dist/subagents/CliSubagentExecutionAdapter.d.ts.map +1 -0
- package/dist/subagents/CliSubagentExecutionAdapter.js +217 -0
- package/dist/subagents/CliSubagentExecutionAdapter.js.map +1 -0
- package/dist/subagents/HostTurnSubagentExecutionAdapter.d.ts +78 -0
- package/dist/subagents/HostTurnSubagentExecutionAdapter.d.ts.map +1 -0
- package/dist/subagents/HostTurnSubagentExecutionAdapter.js +160 -0
- package/dist/subagents/HostTurnSubagentExecutionAdapter.js.map +1 -0
- package/dist/subagents/SubagentRunService.d.ts +103 -0
- package/dist/subagents/SubagentRunService.d.ts.map +1 -0
- package/dist/subagents/SubagentRunService.js +1183 -0
- package/dist/subagents/SubagentRunService.js.map +1 -0
- package/dist/subagents/SubagentScheduler.d.ts +167 -0
- package/dist/subagents/SubagentScheduler.d.ts.map +1 -0
- package/dist/subagents/SubagentScheduler.js +530 -0
- package/dist/subagents/SubagentScheduler.js.map +1 -0
- package/dist/subagents/TeamAgentRouter.d.ts +104 -0
- package/dist/subagents/TeamAgentRouter.d.ts.map +1 -0
- package/dist/subagents/TeamAgentRouter.js +435 -0
- package/dist/subagents/TeamAgentRouter.js.map +1 -0
- package/dist/subagents/capabilityMatcher.d.ts +3 -0
- package/dist/subagents/capabilityMatcher.d.ts.map +1 -0
- package/dist/subagents/capabilityMatcher.js +127 -0
- package/dist/subagents/capabilityMatcher.js.map +1 -0
- package/dist/subagents/subagentRunPort.d.ts +43 -0
- package/dist/subagents/subagentRunPort.d.ts.map +1 -0
- package/dist/subagents/subagentRunPort.js +24 -0
- package/dist/subagents/subagentRunPort.js.map +1 -0
- package/dist/subagents/subagentTypes.d.ts +330 -0
- package/dist/subagents/subagentTypes.d.ts.map +1 -0
- package/dist/subagents/subagentTypes.js +2 -0
- package/dist/subagents/subagentTypes.js.map +1 -0
- package/dist/subagents/subagentWorkspaceAuthority.d.ts +29 -0
- package/dist/subagents/subagentWorkspaceAuthority.d.ts.map +1 -0
- package/dist/subagents/subagentWorkspaceAuthority.js +173 -0
- package/dist/subagents/subagentWorkspaceAuthority.js.map +1 -0
- package/dist/support/SupportDiagnosticsService.d.ts +87 -0
- package/dist/support/SupportDiagnosticsService.d.ts.map +1 -0
- package/dist/support/SupportDiagnosticsService.js +1199 -0
- package/dist/support/SupportDiagnosticsService.js.map +1 -0
- package/dist/turns/DurableInterruptedTurnStore.d.ts +14 -0
- package/dist/turns/DurableInterruptedTurnStore.d.ts.map +1 -0
- package/dist/turns/DurableInterruptedTurnStore.js +63 -0
- package/dist/turns/DurableInterruptedTurnStore.js.map +1 -0
- package/dist/turns/interruptedTurns.d.ts +50 -0
- package/dist/turns/interruptedTurns.d.ts.map +1 -0
- package/dist/turns/interruptedTurns.js +54 -0
- package/dist/turns/interruptedTurns.js.map +1 -0
- package/dist/turns/turnAdmission.d.ts +113 -0
- package/dist/turns/turnAdmission.d.ts.map +1 -0
- package/dist/turns/turnAdmission.js +136 -0
- package/dist/turns/turnAdmission.js.map +1 -0
- package/dist/workspace/WorkspaceHostService.d.ts +126 -0
- package/dist/workspace/WorkspaceHostService.d.ts.map +1 -0
- package/dist/workspace/WorkspaceHostService.js +841 -0
- package/dist/workspace/WorkspaceHostService.js.map +1 -0
- package/dist/workspace/WorkspaceShellService.d.ts +71 -0
- package/dist/workspace/WorkspaceShellService.d.ts.map +1 -0
- package/dist/workspace/WorkspaceShellService.js +273 -0
- package/dist/workspace/WorkspaceShellService.js.map +1 -0
- package/dist/workspace/fileAssociations.d.ts +12 -0
- package/dist/workspace/fileAssociations.d.ts.map +1 -0
- package/dist/workspace/fileAssociations.js +202 -0
- package/dist/workspace/fileAssociations.js.map +1 -0
- package/dist/workspace/scrollbackBuffer.d.ts +48 -0
- package/dist/workspace/scrollbackBuffer.d.ts.map +1 -0
- package/dist/workspace/scrollbackBuffer.js +94 -0
- package/dist/workspace/scrollbackBuffer.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,1183 @@
|
|
|
1
|
+
import { closeSync, copyFileSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, writeSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
import { matchSubagentCapabilities } from './capabilityMatcher.js';
|
|
5
|
+
const ACTIVE_STATUSES = new Set([
|
|
6
|
+
'queued',
|
|
7
|
+
'launch-requested',
|
|
8
|
+
'running',
|
|
9
|
+
'waiting-for-permission',
|
|
10
|
+
'waiting-for-user',
|
|
11
|
+
]);
|
|
12
|
+
const TERMINAL_STATUSES = new Set(['completed', 'failed', 'cancelled']);
|
|
13
|
+
const MAX_STORED_RUNS = 5000;
|
|
14
|
+
const DEFAULT_LEASE_MS = 120_000;
|
|
15
|
+
const DEFAULT_MAX_ATTEMPTS = 3;
|
|
16
|
+
const DEFAULT_RUN_TIMEOUT_MS = 60 * 60_000;
|
|
17
|
+
const DEFAULT_STALE_HEARTBEAT_MS = 5 * 60_000;
|
|
18
|
+
const VALID_EXPECTED_ARTIFACT_TYPES = new Set(['answer', 'file', 'diff', 'diagnostic', 'report']);
|
|
19
|
+
const LOCAL_CLI_APP_SERVER_HOSTS = new Set(['localhost', '127.0.0.1', '::1']);
|
|
20
|
+
const LEGAL_STATUS_TRANSITIONS = {
|
|
21
|
+
queued: new Set(['queued', 'launch-requested', 'running', 'waiting-for-permission', 'waiting-for-user', 'failed', 'cancelled']),
|
|
22
|
+
'launch-requested': new Set(['queued', 'launch-requested', 'running', 'waiting-for-permission', 'waiting-for-user', 'failed', 'cancelled']),
|
|
23
|
+
running: new Set(['queued', 'launch-requested', 'running', 'waiting-for-permission', 'waiting-for-user', 'completed', 'failed', 'cancelled']),
|
|
24
|
+
'waiting-for-permission': new Set(['queued', 'launch-requested', 'waiting-for-permission', 'running', 'waiting-for-user', 'failed', 'cancelled']),
|
|
25
|
+
'waiting-for-user': new Set(['queued', 'launch-requested', 'waiting-for-user', 'running', 'waiting-for-permission', 'failed', 'cancelled']),
|
|
26
|
+
completed: new Set(['completed']),
|
|
27
|
+
failed: new Set(['failed']),
|
|
28
|
+
cancelled: new Set(['cancelled']),
|
|
29
|
+
};
|
|
30
|
+
function readRecord(value) {
|
|
31
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
32
|
+
? value
|
|
33
|
+
: {};
|
|
34
|
+
}
|
|
35
|
+
function requiredString(value, label) {
|
|
36
|
+
if (typeof value !== 'string' || !value.trim())
|
|
37
|
+
throw new Error(`${label} is required.`);
|
|
38
|
+
return value.trim();
|
|
39
|
+
}
|
|
40
|
+
function optionalString(value) {
|
|
41
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
42
|
+
}
|
|
43
|
+
function optionalEnvKey(value) {
|
|
44
|
+
const key = optionalString(value)?.slice(0, 128);
|
|
45
|
+
if (key && /^(sk|xox[baprs]|gh[pousr])_[A-Za-z0-9_=-]{8,}$/i.test(key))
|
|
46
|
+
return undefined;
|
|
47
|
+
return key && /^[A-Za-z_][A-Za-z0-9_]*$/.test(key) ? key : undefined;
|
|
48
|
+
}
|
|
49
|
+
function normalizeCliEndpoint(value) {
|
|
50
|
+
const raw = optionalString(value);
|
|
51
|
+
if (!raw)
|
|
52
|
+
return undefined;
|
|
53
|
+
try {
|
|
54
|
+
const url = new URL(raw.slice(0, 500));
|
|
55
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:')
|
|
56
|
+
return undefined;
|
|
57
|
+
const host = url.hostname.toLowerCase();
|
|
58
|
+
if (!LOCAL_CLI_APP_SERVER_HOSTS.has(host) && !host.endsWith('.localhost'))
|
|
59
|
+
return undefined;
|
|
60
|
+
url.username = '';
|
|
61
|
+
url.password = '';
|
|
62
|
+
url.search = '';
|
|
63
|
+
url.hash = '';
|
|
64
|
+
return url.origin;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function normalizeStringArray(value) {
|
|
71
|
+
if (!Array.isArray(value))
|
|
72
|
+
return [];
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const entry of value) {
|
|
76
|
+
if (typeof entry !== 'string')
|
|
77
|
+
continue;
|
|
78
|
+
const trimmed = entry.trim();
|
|
79
|
+
const key = trimmed.toLowerCase();
|
|
80
|
+
if (!trimmed || seen.has(key))
|
|
81
|
+
continue;
|
|
82
|
+
seen.add(key);
|
|
83
|
+
out.push(trimmed);
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
function normalizeCapabilityMatch(value) {
|
|
88
|
+
const score = typeof value?.score === 'number' && Number.isFinite(value.score)
|
|
89
|
+
? Math.max(0, Math.min(1, value.score))
|
|
90
|
+
: 0.5;
|
|
91
|
+
return {
|
|
92
|
+
score,
|
|
93
|
+
reasons: normalizeStringArray(value?.reasons),
|
|
94
|
+
missingCapabilities: normalizeStringArray(value?.missingCapabilities),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function normalizeToolPolicy(value) {
|
|
98
|
+
const permissionMode = value?.permissionMode === 'ask-parent'
|
|
99
|
+
|| value?.permissionMode === 'ask-user'
|
|
100
|
+
|| value?.permissionMode === 'deny'
|
|
101
|
+
|| value?.permissionMode === 'inherit-parent'
|
|
102
|
+
? value.permissionMode
|
|
103
|
+
: 'inherit-parent';
|
|
104
|
+
return {
|
|
105
|
+
allowedTools: normalizeStringArray(value?.allowedTools),
|
|
106
|
+
allowedDirectories: normalizeStringArray(value?.allowedDirectories),
|
|
107
|
+
allowedApps: normalizeStringArray(value?.allowedApps),
|
|
108
|
+
permissionMode,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function normalizePositiveNumber(value, max) {
|
|
112
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
113
|
+
return undefined;
|
|
114
|
+
const normalized = Math.floor(value);
|
|
115
|
+
return normalized > 0 ? Math.min(normalized, max) : undefined;
|
|
116
|
+
}
|
|
117
|
+
function normalizeBudget(value) {
|
|
118
|
+
if (!value || typeof value !== 'object')
|
|
119
|
+
return undefined;
|
|
120
|
+
const budget = {
|
|
121
|
+
maxTokens: normalizePositiveNumber(value.maxTokens, 10_000_000),
|
|
122
|
+
maxCredits: normalizePositiveNumber(value.maxCredits, 10_000_000),
|
|
123
|
+
estimatedTokens: normalizePositiveNumber(value.estimatedTokens, 10_000_000),
|
|
124
|
+
estimatedCredits: normalizePositiveNumber(value.estimatedCredits, 10_000_000),
|
|
125
|
+
spentTokens: normalizePositiveNumber(value.spentTokens, 10_000_000),
|
|
126
|
+
spentCredits: normalizePositiveNumber(value.spentCredits, 10_000_000),
|
|
127
|
+
};
|
|
128
|
+
return Object.values(budget).some((entry) => entry !== undefined) ? budget : undefined;
|
|
129
|
+
}
|
|
130
|
+
function normalizeExpectedArtifacts(value) {
|
|
131
|
+
if (!Array.isArray(value))
|
|
132
|
+
return ['answer'];
|
|
133
|
+
const out = [];
|
|
134
|
+
for (const entry of value) {
|
|
135
|
+
if (typeof entry !== 'string')
|
|
136
|
+
continue;
|
|
137
|
+
const normalized = entry.trim();
|
|
138
|
+
if (!VALID_EXPECTED_ARTIFACT_TYPES.has(normalized) || out.includes(normalized))
|
|
139
|
+
continue;
|
|
140
|
+
out.push(normalized);
|
|
141
|
+
}
|
|
142
|
+
return out.length ? out.slice(0, 20) : ['answer'];
|
|
143
|
+
}
|
|
144
|
+
function normalizeRuntimeSelectionMetadata(value) {
|
|
145
|
+
const record = readRecord(value);
|
|
146
|
+
const providerKind = optionalString(record.providerKind);
|
|
147
|
+
if (providerKind !== 'sdk-native'
|
|
148
|
+
&& providerKind !== 'cli-app-server'
|
|
149
|
+
&& providerKind !== 'cli-remote'
|
|
150
|
+
&& providerKind !== 'acp-provider')
|
|
151
|
+
return undefined;
|
|
152
|
+
const providerId = optionalString(record.providerId) || providerKind;
|
|
153
|
+
const cliEndpoint = providerKind === 'cli-app-server' ? normalizeCliEndpoint(record.cliAppServerEndpoint) : undefined;
|
|
154
|
+
return {
|
|
155
|
+
providerKind,
|
|
156
|
+
providerId,
|
|
157
|
+
...(providerKind === 'acp-provider' && optionalString(record.acpAgentConfigId) ? { acpAgentConfigId: optionalString(record.acpAgentConfigId) } : {}),
|
|
158
|
+
...(cliEndpoint ? { cliAppServerEndpoint: cliEndpoint } : {}),
|
|
159
|
+
...(cliEndpoint && optionalEnvKey(record.cliAppServerTokenEnvKey) ? { cliAppServerTokenEnvKey: optionalEnvKey(record.cliAppServerTokenEnvKey) } : {}),
|
|
160
|
+
...(providerKind === 'cli-remote' && optionalString(record.remoteBackendId) ? { remoteBackendId: optionalString(record.remoteBackendId) } : {}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function normalizeDelegationPolicy(value) {
|
|
164
|
+
const record = readRecord(value);
|
|
165
|
+
return {
|
|
166
|
+
maxTokens: normalizePositiveNumber(record.maxTokens, 10_000_000),
|
|
167
|
+
maxCredits: normalizePositiveNumber(record.maxCredits, 10_000_000),
|
|
168
|
+
maxRuntimeMs: normalizePositiveNumber(record.maxRuntimeMs, 24 * 60 * 60_000),
|
|
169
|
+
maxConcurrentChildRuns: normalizePositiveNumber(record.maxConcurrentChildRuns, 20),
|
|
170
|
+
maxChildDepth: normalizePositiveNumber(record.maxChildDepth, 10),
|
|
171
|
+
allowFurtherDelegation: record.allowFurtherDelegation === true,
|
|
172
|
+
cancellationPropagation: record.cancellationPropagation !== false,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function normalizeDelegation(value, run, now) {
|
|
176
|
+
if (!value || typeof value !== 'object')
|
|
177
|
+
return undefined;
|
|
178
|
+
const record = readRecord(value);
|
|
179
|
+
const depth = normalizePositiveNumber(record.depth, 10) || 1;
|
|
180
|
+
return {
|
|
181
|
+
routerVersion: 1,
|
|
182
|
+
routedAt: typeof record.routedAt === 'number' && Number.isFinite(record.routedAt) ? record.routedAt : now,
|
|
183
|
+
parentConversationId: optionalString(record.parentConversationId) || run.parentConversationId,
|
|
184
|
+
parentRequestId: optionalString(record.parentRequestId) || run.parentRequestId,
|
|
185
|
+
parentAgentId: optionalString(record.parentAgentId) || run.parentAgentId,
|
|
186
|
+
targetAgentId: optionalString(record.targetAgentId) || run.childAgentId,
|
|
187
|
+
workspaceId: optionalString(record.workspaceId) || run.workspaceId,
|
|
188
|
+
teamId: optionalString(record.teamId) || run.teamId,
|
|
189
|
+
contextAttachmentIds: normalizeStringArray(record.contextAttachmentIds).slice(0, 100),
|
|
190
|
+
expectedArtifacts: normalizeExpectedArtifacts(record.expectedArtifacts),
|
|
191
|
+
targetRuntimeSelection: normalizeRuntimeSelectionMetadata(record.targetRuntimeSelection),
|
|
192
|
+
policy: normalizeDelegationPolicy(record.policy),
|
|
193
|
+
depth,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function assertBudgetAllowsQueue(budget) {
|
|
197
|
+
if (!budget)
|
|
198
|
+
return;
|
|
199
|
+
if (budget.maxTokens !== undefined && budget.estimatedTokens !== undefined && budget.estimatedTokens > budget.maxTokens) {
|
|
200
|
+
throw new Error(`Subagent budget denied: estimated ${budget.estimatedTokens} tokens exceeds ${budget.maxTokens} token limit.`);
|
|
201
|
+
}
|
|
202
|
+
if (budget.maxCredits !== undefined && budget.estimatedCredits !== undefined && budget.estimatedCredits > budget.maxCredits) {
|
|
203
|
+
throw new Error(`Subagent budget denied: estimated ${budget.estimatedCredits} credits exceeds ${budget.maxCredits} credit limit.`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function assertCapabilitiesAllowQueue(match, requiredCapabilities) {
|
|
207
|
+
const missing = new Set(match.missingCapabilities);
|
|
208
|
+
for (const capability of requiredCapabilities) {
|
|
209
|
+
if (missing.has(capability)) {
|
|
210
|
+
throw new Error(`Subagent capability denied: missing required capability "${capability}".`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (match.missingCapabilities.length > 0 && match.score < 0.5) {
|
|
214
|
+
throw new Error(`Subagent capability denied: missing ${match.missingCapabilities.join(', ')}.`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function normalizePriority(value) {
|
|
218
|
+
const numeric = typeof value === 'number' && Number.isFinite(value) ? Math.floor(value) : 0;
|
|
219
|
+
return Math.max(-100, Math.min(100, numeric));
|
|
220
|
+
}
|
|
221
|
+
function normalizeMaxConcurrentRuns(value) {
|
|
222
|
+
const numeric = typeof value === 'number' && Number.isFinite(value) ? Math.floor(value) : 1;
|
|
223
|
+
return Math.max(1, Math.min(20, numeric));
|
|
224
|
+
}
|
|
225
|
+
function nextQueueOrdinal(runs) {
|
|
226
|
+
return runs.reduce((max, run) => Math.max(max, run.queue?.slotOrdinal || 0), 0) + 1;
|
|
227
|
+
}
|
|
228
|
+
function normalizeQueue(value, run, slotOrdinal) {
|
|
229
|
+
const slotKey = optionalString(value?.slotKey) || `${run.workspaceId}:${run.teamId}:${run.childAgentId}`;
|
|
230
|
+
return {
|
|
231
|
+
queuedAt: run.createdAt,
|
|
232
|
+
priority: normalizePriority(value?.priority),
|
|
233
|
+
slotKey,
|
|
234
|
+
slotOrdinal,
|
|
235
|
+
maxConcurrentRuns: normalizeMaxConcurrentRuns(value?.maxConcurrentRuns),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function normalizeCancellation(request, run, now) {
|
|
239
|
+
const requestedPropagation = request.propagateToChild !== false;
|
|
240
|
+
const propagateToChild = requestedPropagation && (!!run.childRequestId || run.status === 'launch-requested' || run.status === 'running');
|
|
241
|
+
const requestedFrom = request.requestedFrom === 'parent'
|
|
242
|
+
|| request.requestedFrom === 'child'
|
|
243
|
+
|| request.requestedFrom === 'scheduler'
|
|
244
|
+
|| request.requestedFrom === 'user'
|
|
245
|
+
|| request.requestedFrom === 'unknown'
|
|
246
|
+
? request.requestedFrom
|
|
247
|
+
: 'unknown';
|
|
248
|
+
return {
|
|
249
|
+
requestedAt: now,
|
|
250
|
+
requestedBy: optionalString(request.requestedBy),
|
|
251
|
+
requestedFrom,
|
|
252
|
+
reason: truncateSummary(request.reason),
|
|
253
|
+
parentConversationId: run.parentConversationId,
|
|
254
|
+
parentRequestId: run.parentRequestId,
|
|
255
|
+
childConversationId: run.childConversationId,
|
|
256
|
+
childRequestId: run.childRequestId,
|
|
257
|
+
parentAgentId: run.parentAgentId,
|
|
258
|
+
childAgentId: run.childAgentId,
|
|
259
|
+
propagateToChild,
|
|
260
|
+
propagatedAt: propagateToChild && run.childRequestId ? (request.propagatedAt || now) : undefined,
|
|
261
|
+
propagationStatus: propagateToChild ? (run.childRequestId ? 'propagated' : 'pending') : 'not-applicable',
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function normalizeHandoff(value, run, artifact, now) {
|
|
265
|
+
const record = readRecord(value);
|
|
266
|
+
const provenance = readRecord(record.provenance);
|
|
267
|
+
return {
|
|
268
|
+
subagentRunId: optionalString(record.subagentRunId) || run.id,
|
|
269
|
+
parentConversationId: optionalString(record.parentConversationId) || run.parentConversationId,
|
|
270
|
+
childConversationId: optionalString(record.childConversationId) || run.childConversationId,
|
|
271
|
+
parentAgentId: optionalString(record.parentAgentId) || run.parentAgentId,
|
|
272
|
+
childAgentId: optionalString(record.childAgentId) || run.childAgentId,
|
|
273
|
+
parentRequestId: optionalString(record.parentRequestId) || run.parentRequestId,
|
|
274
|
+
childRequestId: optionalString(record.childRequestId) || run.childRequestId,
|
|
275
|
+
handedOffAt: typeof record.handedOffAt === 'number' && Number.isFinite(record.handedOffAt) ? record.handedOffAt : now,
|
|
276
|
+
provenance: {
|
|
277
|
+
createdByAgentId: optionalString(provenance.createdByAgentId) || run.childAgentId,
|
|
278
|
+
createdByConversationId: optionalString(provenance.createdByConversationId) || artifact.conversationId || run.childConversationId,
|
|
279
|
+
createdByRequestId: optionalString(provenance.createdByRequestId) || run.childRequestId,
|
|
280
|
+
sourcePath: optionalString(provenance.sourcePath) || artifact.path,
|
|
281
|
+
sourceMessageId: optionalString(provenance.sourceMessageId) || artifact.messageId,
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function normalizeRecoveryHistory(value) {
|
|
286
|
+
if (!Array.isArray(value))
|
|
287
|
+
return undefined;
|
|
288
|
+
const events = value.flatMap((entry) => {
|
|
289
|
+
const record = readRecord(entry);
|
|
290
|
+
const action = record.action === 'requeued' || record.action === 'failed' || record.action === 'cancelled'
|
|
291
|
+
? record.action
|
|
292
|
+
: undefined;
|
|
293
|
+
const reason = record.reason === 'lease-expired'
|
|
294
|
+
|| record.reason === 'heartbeat-stale'
|
|
295
|
+
|| record.reason === 'timeout'
|
|
296
|
+
|| record.reason === 'manual-cancel'
|
|
297
|
+
|| record.reason === 'scheduler-cancel'
|
|
298
|
+
? record.reason
|
|
299
|
+
: undefined;
|
|
300
|
+
const at = typeof record.at === 'number' && Number.isFinite(record.at) ? record.at : undefined;
|
|
301
|
+
if (!action || !reason || at === undefined)
|
|
302
|
+
return [];
|
|
303
|
+
return [{
|
|
304
|
+
at,
|
|
305
|
+
action,
|
|
306
|
+
reason,
|
|
307
|
+
workerId: optionalString(record.workerId),
|
|
308
|
+
attempt: typeof record.attempt === 'number' && Number.isFinite(record.attempt) ? record.attempt : undefined,
|
|
309
|
+
detail: optionalString(record.detail),
|
|
310
|
+
}];
|
|
311
|
+
});
|
|
312
|
+
return events.length ? events.slice(-100) : undefined;
|
|
313
|
+
}
|
|
314
|
+
function normalizeRecordMetadata(value) {
|
|
315
|
+
const record = readRecord(value);
|
|
316
|
+
return Object.keys(record).length ? record : undefined;
|
|
317
|
+
}
|
|
318
|
+
function normalizeAttemptStatus(value) {
|
|
319
|
+
if (value === 'launch-requested'
|
|
320
|
+
|| value === 'running'
|
|
321
|
+
|| value === 'completed'
|
|
322
|
+
|| value === 'failed'
|
|
323
|
+
|| value === 'cancelled'
|
|
324
|
+
|| value === 'expired')
|
|
325
|
+
return value;
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
function normalizeExecutionAttempts(value) {
|
|
329
|
+
if (!Array.isArray(value))
|
|
330
|
+
return undefined;
|
|
331
|
+
const attempts = value.flatMap((entry) => {
|
|
332
|
+
const record = readRecord(entry);
|
|
333
|
+
const attempt = typeof record.attempt === 'number' && Number.isFinite(record.attempt)
|
|
334
|
+
? Math.max(1, Math.floor(record.attempt))
|
|
335
|
+
: undefined;
|
|
336
|
+
const workerId = optionalString(record.workerId);
|
|
337
|
+
const status = normalizeAttemptStatus(record.status);
|
|
338
|
+
const launchRequestedAt = typeof record.launchRequestedAt === 'number' && Number.isFinite(record.launchRequestedAt)
|
|
339
|
+
? record.launchRequestedAt
|
|
340
|
+
: undefined;
|
|
341
|
+
if (!attempt || !workerId || !status || launchRequestedAt === undefined)
|
|
342
|
+
return [];
|
|
343
|
+
return [{
|
|
344
|
+
attempt,
|
|
345
|
+
workerId,
|
|
346
|
+
adapterId: optionalString(record.adapterId),
|
|
347
|
+
adapterVersion: optionalString(record.adapterVersion),
|
|
348
|
+
adapterCapabilities: normalizeStringArray(record.adapterCapabilities),
|
|
349
|
+
status,
|
|
350
|
+
launchRequestedAt,
|
|
351
|
+
launchRequestExpiresAt: typeof record.launchRequestExpiresAt === 'number' && Number.isFinite(record.launchRequestExpiresAt) ? record.launchRequestExpiresAt : undefined,
|
|
352
|
+
runningAt: typeof record.runningAt === 'number' && Number.isFinite(record.runningAt) ? record.runningAt : undefined,
|
|
353
|
+
endedAt: typeof record.endedAt === 'number' && Number.isFinite(record.endedAt) ? record.endedAt : undefined,
|
|
354
|
+
childRequestId: optionalString(record.childRequestId),
|
|
355
|
+
error: truncateSummary(optionalString(record.error)),
|
|
356
|
+
metadata: normalizeRecordMetadata(record.metadata),
|
|
357
|
+
}];
|
|
358
|
+
});
|
|
359
|
+
return attempts.length ? attempts.slice(-100) : undefined;
|
|
360
|
+
}
|
|
361
|
+
function markLatestAttempt(run, status, now, patch = {}) {
|
|
362
|
+
const attempts = [...(run.executionAttempts || [])];
|
|
363
|
+
const index = attempts.length - 1;
|
|
364
|
+
if (index < 0)
|
|
365
|
+
return run.executionAttempts;
|
|
366
|
+
const current = attempts[index];
|
|
367
|
+
attempts[index] = {
|
|
368
|
+
...current,
|
|
369
|
+
...patch,
|
|
370
|
+
status,
|
|
371
|
+
runningAt: status === 'running' ? (current.runningAt || now) : (patch.runningAt ?? current.runningAt),
|
|
372
|
+
endedAt: status === 'completed' || status === 'failed' || status === 'cancelled' || status === 'expired'
|
|
373
|
+
? (current.endedAt || now)
|
|
374
|
+
: (patch.endedAt ?? current.endedAt),
|
|
375
|
+
};
|
|
376
|
+
return attempts.slice(-100);
|
|
377
|
+
}
|
|
378
|
+
function appendRecoveryHistory(run, event) {
|
|
379
|
+
return [...(run.recoveryHistory || []), event].slice(-100);
|
|
380
|
+
}
|
|
381
|
+
function normalizeArtifact(value, run, now) {
|
|
382
|
+
const record = readRecord(value);
|
|
383
|
+
const title = optionalString(record.title);
|
|
384
|
+
if (!title)
|
|
385
|
+
return null;
|
|
386
|
+
const type = record.type === 'file'
|
|
387
|
+
|| record.type === 'diff'
|
|
388
|
+
|| record.type === 'terminal'
|
|
389
|
+
|| record.type === 'browser'
|
|
390
|
+
|| record.type === 'note'
|
|
391
|
+
|| record.type === 'unknown'
|
|
392
|
+
? record.type
|
|
393
|
+
: 'unknown';
|
|
394
|
+
const artifact = {
|
|
395
|
+
id: optionalString(record.id) || `artifact-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
396
|
+
type,
|
|
397
|
+
title,
|
|
398
|
+
path: optionalString(record.path),
|
|
399
|
+
conversationId: optionalString(record.conversationId),
|
|
400
|
+
messageId: optionalString(record.messageId),
|
|
401
|
+
metadata: readRecord(record.metadata),
|
|
402
|
+
createdAt: typeof record.createdAt === 'number' && Number.isFinite(record.createdAt) ? record.createdAt : now,
|
|
403
|
+
};
|
|
404
|
+
return {
|
|
405
|
+
...artifact,
|
|
406
|
+
handoff: normalizeHandoff(record.handoff, run, artifact, now),
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function truncateSummary(value) {
|
|
410
|
+
if (!value)
|
|
411
|
+
return undefined;
|
|
412
|
+
const trimmed = value.trim();
|
|
413
|
+
if (!trimmed)
|
|
414
|
+
return undefined;
|
|
415
|
+
return trimmed.length > 12000 ? `${trimmed.slice(0, 12000)}\n...[truncated]` : trimmed;
|
|
416
|
+
}
|
|
417
|
+
function clampLeaseMs(value) {
|
|
418
|
+
const numeric = typeof value === 'number' && Number.isFinite(value) ? value : DEFAULT_LEASE_MS;
|
|
419
|
+
return Math.max(10_000, Math.min(15 * 60_000, Math.floor(numeric)));
|
|
420
|
+
}
|
|
421
|
+
function clampRunTimeoutMs(value) {
|
|
422
|
+
const numeric = typeof value === 'number' && Number.isFinite(value) ? value : DEFAULT_RUN_TIMEOUT_MS;
|
|
423
|
+
return Math.max(30_000, Math.min(24 * 60 * 60_000, Math.floor(numeric)));
|
|
424
|
+
}
|
|
425
|
+
function clampStaleHeartbeatMs(value) {
|
|
426
|
+
const numeric = typeof value === 'number' && Number.isFinite(value) ? value : DEFAULT_STALE_HEARTBEAT_MS;
|
|
427
|
+
return Math.max(10_000, Math.min(60 * 60_000, Math.floor(numeric)));
|
|
428
|
+
}
|
|
429
|
+
function activeLeaseExpired(run, now) {
|
|
430
|
+
return ACTIVE_STATUSES.has(run.status) && typeof run.leaseExpiresAt === 'number' && run.leaseExpiresAt <= now;
|
|
431
|
+
}
|
|
432
|
+
function activeRunTimedOut(run, now, timeoutMs) {
|
|
433
|
+
return ACTIVE_STATUSES.has(run.status)
|
|
434
|
+
&& run.status !== 'queued'
|
|
435
|
+
&& typeof run.startedAt === 'number'
|
|
436
|
+
&& run.startedAt + timeoutMs <= now;
|
|
437
|
+
}
|
|
438
|
+
function activeHeartbeatStale(run, now, staleHeartbeatMs) {
|
|
439
|
+
if (!ACTIVE_STATUSES.has(run.status) || run.status !== 'running' || !run.leaseOwner)
|
|
440
|
+
return false;
|
|
441
|
+
const heartbeatAt = typeof run.heartbeatAt === 'number' ? run.heartbeatAt : run.startedAt;
|
|
442
|
+
return typeof heartbeatAt === 'number' && heartbeatAt + staleHeartbeatMs <= now;
|
|
443
|
+
}
|
|
444
|
+
function assertStatusTransition(from, to) {
|
|
445
|
+
if (LEGAL_STATUS_TRANSITIONS[from]?.has(to))
|
|
446
|
+
return;
|
|
447
|
+
throw new Error(`Illegal subagent run status transition: ${from} -> ${to}.`);
|
|
448
|
+
}
|
|
449
|
+
function isSubagentRunStatus(value) {
|
|
450
|
+
return value === 'queued'
|
|
451
|
+
|| value === 'launch-requested'
|
|
452
|
+
|| value === 'running'
|
|
453
|
+
|| value === 'waiting-for-permission'
|
|
454
|
+
|| value === 'waiting-for-user'
|
|
455
|
+
|| value === 'completed'
|
|
456
|
+
|| value === 'failed'
|
|
457
|
+
|| value === 'cancelled';
|
|
458
|
+
}
|
|
459
|
+
function isPersistedSubagentRun(value) {
|
|
460
|
+
const run = readRecord(value);
|
|
461
|
+
return typeof run.id === 'string'
|
|
462
|
+
&& typeof run.parentConversationId === 'string'
|
|
463
|
+
&& typeof run.parentRequestId === 'string'
|
|
464
|
+
&& typeof run.parentAgentId === 'string'
|
|
465
|
+
&& typeof run.childConversationId === 'string'
|
|
466
|
+
&& typeof run.childAgentId === 'string'
|
|
467
|
+
&& typeof run.workspaceId === 'string'
|
|
468
|
+
&& typeof run.teamId === 'string'
|
|
469
|
+
&& typeof run.createdAt === 'number'
|
|
470
|
+
&& typeof run.updatedAt === 'number'
|
|
471
|
+
&& isSubagentRunStatus(run.status)
|
|
472
|
+
&& typeof run.objective === 'string'
|
|
473
|
+
&& Array.isArray(run.artifacts);
|
|
474
|
+
}
|
|
475
|
+
export class SubagentRunService {
|
|
476
|
+
storePath;
|
|
477
|
+
/**
|
|
478
|
+
* ⚠️ The default is the REAL user's home, and a host that runs isolated must not use it.
|
|
479
|
+
* `readonly` and public so `getSubagentRunService` can refuse a second, conflicting store.
|
|
480
|
+
*/
|
|
481
|
+
constructor(storePath = join(homedir(), '.xeno', 'subagent-runs.json')) {
|
|
482
|
+
this.storePath = storePath;
|
|
483
|
+
}
|
|
484
|
+
createRun(request) {
|
|
485
|
+
try {
|
|
486
|
+
const now = Date.now();
|
|
487
|
+
const capabilityMatch = normalizeCapabilityMatch(request.capabilityMatch);
|
|
488
|
+
const requiredCapabilities = normalizeStringArray(request.requiredCapabilities);
|
|
489
|
+
assertCapabilitiesAllowQueue(capabilityMatch, requiredCapabilities);
|
|
490
|
+
const budget = normalizeBudget(request.budget);
|
|
491
|
+
assertBudgetAllowsQueue(budget);
|
|
492
|
+
const store = this.load();
|
|
493
|
+
const baseRun = {
|
|
494
|
+
workspaceId: requiredString(request.workspaceId, 'workspaceId'),
|
|
495
|
+
teamId: requiredString(request.teamId, 'teamId'),
|
|
496
|
+
childAgentId: requiredString(request.childAgentId, 'childAgentId'),
|
|
497
|
+
createdAt: now,
|
|
498
|
+
};
|
|
499
|
+
const run = {
|
|
500
|
+
id: `subrun-${now}-${Math.random().toString(36).slice(2, 9)}`,
|
|
501
|
+
parentConversationId: requiredString(request.parentConversationId, 'parentConversationId'),
|
|
502
|
+
parentRequestId: optionalString(request.parentRequestId) || `parent-${now}`,
|
|
503
|
+
parentAgentId: requiredString(request.parentAgentId, 'parentAgentId'),
|
|
504
|
+
childConversationId: requiredString(request.childConversationId, 'childConversationId'),
|
|
505
|
+
childAgentId: baseRun.childAgentId,
|
|
506
|
+
workspaceId: baseRun.workspaceId,
|
|
507
|
+
teamId: baseRun.teamId,
|
|
508
|
+
createdAt: now,
|
|
509
|
+
updatedAt: now,
|
|
510
|
+
status: 'queued',
|
|
511
|
+
objective: requiredString(request.objective, 'objective'),
|
|
512
|
+
role: optionalString(request.role),
|
|
513
|
+
// Bounded by the parent before it reaches here. Persisted with the run because the run may
|
|
514
|
+
// be launched long after the parent turn ended, so re-deriving the bound at launch time
|
|
515
|
+
// would ask a question whose answer has already gone.
|
|
516
|
+
workspace: request.workspace ? structuredClone(request.workspace) : undefined,
|
|
517
|
+
capabilityMatch,
|
|
518
|
+
toolPolicy: normalizeToolPolicy(request.toolPolicy),
|
|
519
|
+
parentAgentName: optionalString(request.parentAgentName),
|
|
520
|
+
childAgentName: optionalString(request.childAgentName),
|
|
521
|
+
workspaceName: optionalString(request.workspaceName),
|
|
522
|
+
teamName: optionalString(request.teamName),
|
|
523
|
+
artifacts: [],
|
|
524
|
+
...(budget ? { budget } : {}),
|
|
525
|
+
queue: normalizeQueue(request.queue, baseRun, nextQueueOrdinal(store.runs)),
|
|
526
|
+
};
|
|
527
|
+
const delegation = normalizeDelegation(request.delegation, run, now);
|
|
528
|
+
if (delegation)
|
|
529
|
+
run.delegation = delegation;
|
|
530
|
+
store.runs.unshift(run);
|
|
531
|
+
store.runs = store.runs.slice(0, MAX_STORED_RUNS);
|
|
532
|
+
this.save(store);
|
|
533
|
+
return { success: true, run };
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
updateRun(request) {
|
|
540
|
+
try {
|
|
541
|
+
const id = requiredString(request.id, 'id');
|
|
542
|
+
const store = this.load();
|
|
543
|
+
const index = store.runs.findIndex((run) => run.id === id);
|
|
544
|
+
if (index < 0)
|
|
545
|
+
return { success: false, error: 'Subagent run not found.' };
|
|
546
|
+
const current = store.runs[index];
|
|
547
|
+
const now = Date.now();
|
|
548
|
+
const status = request.status || current.status;
|
|
549
|
+
assertStatusTransition(current.status, status);
|
|
550
|
+
const workerId = optionalString(request.workerId) || current.leaseOwner || current.launchOwner || 'legacy-renderer';
|
|
551
|
+
const adapterId = optionalString(request.adapterId) || current.executionAdapterId;
|
|
552
|
+
const childRequestId = optionalString(request.childRequestId) || current.childRequestId;
|
|
553
|
+
const artifacts = request.artifacts
|
|
554
|
+
? request.artifacts.map((artifact) => normalizeArtifact(artifact, current, now)).filter((artifact) => !!artifact)
|
|
555
|
+
: current.artifacts;
|
|
556
|
+
const terminalReason = status === 'completed'
|
|
557
|
+
? 'completed'
|
|
558
|
+
: status === 'cancelled'
|
|
559
|
+
? 'cancelled'
|
|
560
|
+
: status === 'failed'
|
|
561
|
+
? (current.terminalReason || 'failed')
|
|
562
|
+
: current.terminalReason;
|
|
563
|
+
const hasAttempts = !!current.executionAttempts?.length;
|
|
564
|
+
const runningAttempts = status === 'running'
|
|
565
|
+
? (hasAttempts
|
|
566
|
+
? markLatestAttempt(current, 'running', request.startedAt || now, { childRequestId, workerId, adapterId })
|
|
567
|
+
: [{
|
|
568
|
+
attempt: current.attempt || 1,
|
|
569
|
+
workerId,
|
|
570
|
+
adapterId,
|
|
571
|
+
status: 'running',
|
|
572
|
+
launchRequestedAt: request.startedAt || now,
|
|
573
|
+
runningAt: request.startedAt || now,
|
|
574
|
+
childRequestId,
|
|
575
|
+
metadata: current.executionMetadata,
|
|
576
|
+
}])
|
|
577
|
+
: current.executionAttempts;
|
|
578
|
+
const terminalAttempts = TERMINAL_STATUSES.has(status)
|
|
579
|
+
? markLatestAttempt({ ...current, executionAttempts: runningAttempts }, status === 'completed' ? 'completed' : status === 'cancelled' ? 'cancelled' : 'failed', request.endedAt || now, { childRequestId, error: truncateSummary(request.error) })
|
|
580
|
+
: runningAttempts;
|
|
581
|
+
const updated = {
|
|
582
|
+
...current,
|
|
583
|
+
parentRequestId: optionalString(request.parentRequestId) || current.parentRequestId,
|
|
584
|
+
childConversationId: optionalString(request.childConversationId) || current.childConversationId,
|
|
585
|
+
childRequestId,
|
|
586
|
+
status,
|
|
587
|
+
startedAt: request.startedAt || current.startedAt || (status === 'running' ? now : undefined),
|
|
588
|
+
endedAt: request.endedAt || current.endedAt || (TERMINAL_STATUSES.has(status) ? now : undefined),
|
|
589
|
+
updatedAt: now,
|
|
590
|
+
summary: truncateSummary(request.summary) ?? current.summary,
|
|
591
|
+
error: truncateSummary(request.error) ?? current.error,
|
|
592
|
+
artifacts,
|
|
593
|
+
tokenUsage: request.tokenUsage || current.tokenUsage,
|
|
594
|
+
leaseOwner: TERMINAL_STATUSES.has(status) ? undefined : current.leaseOwner,
|
|
595
|
+
leaseExpiresAt: TERMINAL_STATUSES.has(status) ? undefined : current.leaseExpiresAt,
|
|
596
|
+
heartbeatAt: TERMINAL_STATUSES.has(status) ? undefined : current.heartbeatAt,
|
|
597
|
+
launchOwner: TERMINAL_STATUSES.has(status) ? undefined : current.launchOwner,
|
|
598
|
+
launchRequestExpiresAt: TERMINAL_STATUSES.has(status) ? undefined : current.launchRequestExpiresAt,
|
|
599
|
+
executionAdapterId: adapterId,
|
|
600
|
+
executionAttempts: terminalAttempts,
|
|
601
|
+
attempt: status === 'running' && !current.attempt ? 1 : current.attempt,
|
|
602
|
+
cancelledAt: status === 'cancelled' ? (current.cancelledAt || now) : current.cancelledAt,
|
|
603
|
+
terminalReason,
|
|
604
|
+
};
|
|
605
|
+
store.runs[index] = updated;
|
|
606
|
+
this.save(store);
|
|
607
|
+
return { success: true, run: updated };
|
|
608
|
+
}
|
|
609
|
+
catch (err) {
|
|
610
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
claimNextRun(request) {
|
|
614
|
+
try {
|
|
615
|
+
const workerId = requiredString(request.workerId, 'workerId');
|
|
616
|
+
const now = typeof request.now === 'number' && Number.isFinite(request.now) ? request.now : Date.now();
|
|
617
|
+
const leaseMs = clampLeaseMs(request.leaseMs);
|
|
618
|
+
const requestedRunId = optionalString(request.id);
|
|
619
|
+
const requestedSlotKey = optionalString(request.slotKey);
|
|
620
|
+
const requestedMaxConcurrentRuns = normalizeMaxConcurrentRuns(request.maxConcurrentRuns);
|
|
621
|
+
const adapterId = optionalString(request.adapterId) || 'renderer-agent-run';
|
|
622
|
+
const adapterVersion = optionalString(request.adapterVersion);
|
|
623
|
+
const adapterCapabilities = normalizeStringArray(request.adapterCapabilities);
|
|
624
|
+
const executionMetadata = normalizeRecordMetadata(request.executionMetadata);
|
|
625
|
+
const store = this.load();
|
|
626
|
+
const orderedCandidates = store.runs
|
|
627
|
+
.map((run, index) => ({ run, index }))
|
|
628
|
+
.filter(({ run }) => {
|
|
629
|
+
if (requestedRunId && run.id !== requestedRunId)
|
|
630
|
+
return false;
|
|
631
|
+
if (request.workspaceId && run.workspaceId !== request.workspaceId)
|
|
632
|
+
return false;
|
|
633
|
+
if (request.teamId && run.teamId !== request.teamId)
|
|
634
|
+
return false;
|
|
635
|
+
if (request.childAgentId && run.childAgentId !== request.childAgentId)
|
|
636
|
+
return false;
|
|
637
|
+
if (requestedSlotKey && (run.queue?.slotKey || `${run.workspaceId}:${run.teamId}:${run.childAgentId}`) !== requestedSlotKey)
|
|
638
|
+
return false;
|
|
639
|
+
if (TERMINAL_STATUSES.has(run.status))
|
|
640
|
+
return false;
|
|
641
|
+
return run.status === 'queued' || activeLeaseExpired(run, now);
|
|
642
|
+
})
|
|
643
|
+
.sort((a, b) => {
|
|
644
|
+
const priorityDelta = (b.run.queue?.priority || 0) - (a.run.queue?.priority || 0);
|
|
645
|
+
if (priorityDelta !== 0)
|
|
646
|
+
return priorityDelta;
|
|
647
|
+
const ordinalDelta = (a.run.queue?.slotOrdinal || Number.MAX_SAFE_INTEGER) - (b.run.queue?.slotOrdinal || Number.MAX_SAFE_INTEGER);
|
|
648
|
+
if (ordinalDelta !== 0)
|
|
649
|
+
return ordinalDelta;
|
|
650
|
+
return a.run.createdAt - b.run.createdAt;
|
|
651
|
+
});
|
|
652
|
+
const candidateIndex = orderedCandidates
|
|
653
|
+
.find(({ run }) => {
|
|
654
|
+
const slotKey = run.queue?.slotKey || `${run.workspaceId}:${run.teamId}:${run.childAgentId}`;
|
|
655
|
+
const maxConcurrentRuns = run.queue?.maxConcurrentRuns || requestedMaxConcurrentRuns;
|
|
656
|
+
if (requestedRunId) {
|
|
657
|
+
const blocking = store.runs
|
|
658
|
+
.filter((queued) => queued.id !== run.id && queued.status === 'queued')
|
|
659
|
+
.filter((queued) => (queued.queue?.slotKey || `${queued.workspaceId}:${queued.teamId}:${queued.childAgentId}`) === slotKey)
|
|
660
|
+
.sort((a, b) => {
|
|
661
|
+
const priorityDelta = (b.queue?.priority || 0) - (a.queue?.priority || 0);
|
|
662
|
+
if (priorityDelta !== 0)
|
|
663
|
+
return priorityDelta;
|
|
664
|
+
const ordinalDelta = (a.queue?.slotOrdinal || Number.MAX_SAFE_INTEGER) - (b.queue?.slotOrdinal || Number.MAX_SAFE_INTEGER);
|
|
665
|
+
if (ordinalDelta !== 0)
|
|
666
|
+
return ordinalDelta;
|
|
667
|
+
return a.createdAt - b.createdAt;
|
|
668
|
+
})[0];
|
|
669
|
+
if (blocking) {
|
|
670
|
+
const priorityDelta = (blocking.queue?.priority || 0) - (run.queue?.priority || 0);
|
|
671
|
+
const blockingComesFirst = priorityDelta > 0
|
|
672
|
+
|| (priorityDelta === 0 && (blocking.queue?.slotOrdinal || Number.MAX_SAFE_INTEGER) < (run.queue?.slotOrdinal || Number.MAX_SAFE_INTEGER))
|
|
673
|
+
|| (priorityDelta === 0
|
|
674
|
+
&& (blocking.queue?.slotOrdinal || Number.MAX_SAFE_INTEGER) === (run.queue?.slotOrdinal || Number.MAX_SAFE_INTEGER)
|
|
675
|
+
&& blocking.createdAt < run.createdAt);
|
|
676
|
+
if (blockingComesFirst)
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
const activeInSlot = store.runs.filter((active) => {
|
|
681
|
+
if (active.id === run.id)
|
|
682
|
+
return false;
|
|
683
|
+
if (active.status !== 'launch-requested' && active.status !== 'running' && active.status !== 'waiting-for-permission' && active.status !== 'waiting-for-user')
|
|
684
|
+
return false;
|
|
685
|
+
const activeSlotKey = active.queue?.slotKey || `${active.workspaceId}:${active.teamId}:${active.childAgentId}`;
|
|
686
|
+
return activeSlotKey === slotKey && !activeLeaseExpired(active, now);
|
|
687
|
+
}).length;
|
|
688
|
+
return activeInSlot < maxConcurrentRuns;
|
|
689
|
+
})?.index;
|
|
690
|
+
if (candidateIndex === undefined)
|
|
691
|
+
return { success: true };
|
|
692
|
+
const current = store.runs[candidateIndex];
|
|
693
|
+
assertStatusTransition(current.status, 'launch-requested');
|
|
694
|
+
const expired = activeLeaseExpired(current, now);
|
|
695
|
+
const nextAttempt = (current.attempt || 0) + 1;
|
|
696
|
+
const expiredAttempts = expired
|
|
697
|
+
? markLatestAttempt(current, 'expired', now, {
|
|
698
|
+
error: truncateSummary(`Recovered expired launch lease from ${current.leaseOwner || current.launchOwner || 'unknown worker'}.`),
|
|
699
|
+
})
|
|
700
|
+
: current.executionAttempts;
|
|
701
|
+
const updated = {
|
|
702
|
+
...current,
|
|
703
|
+
status: 'launch-requested',
|
|
704
|
+
updatedAt: now,
|
|
705
|
+
leaseOwner: workerId,
|
|
706
|
+
launchOwner: workerId,
|
|
707
|
+
launchRequestedAt: now,
|
|
708
|
+
launchRequestExpiresAt: now + leaseMs,
|
|
709
|
+
leaseExpiresAt: now + leaseMs,
|
|
710
|
+
heartbeatAt: now,
|
|
711
|
+
attempt: nextAttempt,
|
|
712
|
+
executionAdapterId: adapterId,
|
|
713
|
+
executionMetadata,
|
|
714
|
+
executionAttempts: [
|
|
715
|
+
...(expiredAttempts || []),
|
|
716
|
+
{
|
|
717
|
+
attempt: nextAttempt,
|
|
718
|
+
workerId,
|
|
719
|
+
adapterId,
|
|
720
|
+
adapterVersion,
|
|
721
|
+
adapterCapabilities,
|
|
722
|
+
status: 'launch-requested',
|
|
723
|
+
launchRequestedAt: now,
|
|
724
|
+
launchRequestExpiresAt: now + leaseMs,
|
|
725
|
+
metadata: executionMetadata,
|
|
726
|
+
},
|
|
727
|
+
].slice(-100),
|
|
728
|
+
error: expired
|
|
729
|
+
? truncateSummary(`Recovered expired lease from ${current.leaseOwner || 'unknown worker'}.`)
|
|
730
|
+
: current.error,
|
|
731
|
+
};
|
|
732
|
+
store.runs[candidateIndex] = updated;
|
|
733
|
+
this.save(store);
|
|
734
|
+
return { success: true, run: updated };
|
|
735
|
+
}
|
|
736
|
+
catch (err) {
|
|
737
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
heartbeatRun(request) {
|
|
741
|
+
try {
|
|
742
|
+
const id = requiredString(request.id, 'id');
|
|
743
|
+
const workerId = requiredString(request.workerId, 'workerId');
|
|
744
|
+
const now = typeof request.now === 'number' && Number.isFinite(request.now) ? request.now : Date.now();
|
|
745
|
+
const leaseMs = clampLeaseMs(request.leaseMs);
|
|
746
|
+
const store = this.load();
|
|
747
|
+
const index = store.runs.findIndex((run) => run.id === id);
|
|
748
|
+
if (index < 0)
|
|
749
|
+
return { success: false, error: 'Subagent run not found.' };
|
|
750
|
+
const current = store.runs[index];
|
|
751
|
+
if (TERMINAL_STATUSES.has(current.status))
|
|
752
|
+
return { success: false, error: 'Subagent run is already terminal.' };
|
|
753
|
+
if (activeLeaseExpired(current, now))
|
|
754
|
+
return { success: false, error: 'Subagent run lease has expired.' };
|
|
755
|
+
if (current.leaseOwner && current.leaseOwner !== workerId)
|
|
756
|
+
return { success: false, error: 'Subagent run is leased by another worker.' };
|
|
757
|
+
const status = current.status === 'queued' || current.status === 'launch-requested' ? 'running' : current.status;
|
|
758
|
+
assertStatusTransition(current.status, status);
|
|
759
|
+
const childRequestId = optionalString(request.childRequestId) || current.childRequestId;
|
|
760
|
+
const adapterId = optionalString(request.adapterId) || current.executionAdapterId;
|
|
761
|
+
const executionAttempts = status === 'running'
|
|
762
|
+
? markLatestAttempt(current, 'running', now, { workerId, childRequestId, adapterId })
|
|
763
|
+
: current.executionAttempts;
|
|
764
|
+
const updated = {
|
|
765
|
+
...current,
|
|
766
|
+
childRequestId,
|
|
767
|
+
status,
|
|
768
|
+
startedAt: current.startedAt || now,
|
|
769
|
+
updatedAt: now,
|
|
770
|
+
leaseOwner: workerId,
|
|
771
|
+
leaseExpiresAt: now + leaseMs,
|
|
772
|
+
heartbeatAt: now,
|
|
773
|
+
launchOwner: current.launchOwner || workerId,
|
|
774
|
+
executionAdapterId: adapterId,
|
|
775
|
+
executionAttempts,
|
|
776
|
+
};
|
|
777
|
+
store.runs[index] = updated;
|
|
778
|
+
this.save(store);
|
|
779
|
+
return { success: true, run: updated };
|
|
780
|
+
}
|
|
781
|
+
catch (err) {
|
|
782
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
cancelRun(request) {
|
|
786
|
+
try {
|
|
787
|
+
const id = requiredString(request.id, 'id');
|
|
788
|
+
const store = this.load();
|
|
789
|
+
const index = store.runs.findIndex((run) => run.id === id);
|
|
790
|
+
if (index < 0)
|
|
791
|
+
return { success: false, error: 'Subagent run not found.' };
|
|
792
|
+
const current = store.runs[index];
|
|
793
|
+
const now = Date.now();
|
|
794
|
+
const status = TERMINAL_STATUSES.has(current.status) ? current.status : 'cancelled';
|
|
795
|
+
assertStatusTransition(current.status, status);
|
|
796
|
+
const executionAttempts = status === 'cancelled' && !TERMINAL_STATUSES.has(current.status)
|
|
797
|
+
? markLatestAttempt(current, 'cancelled', now, { error: truncateSummary(request.reason) })
|
|
798
|
+
: current.executionAttempts;
|
|
799
|
+
const updated = {
|
|
800
|
+
...current,
|
|
801
|
+
status,
|
|
802
|
+
endedAt: current.endedAt || now,
|
|
803
|
+
updatedAt: now,
|
|
804
|
+
leaseOwner: undefined,
|
|
805
|
+
leaseExpiresAt: undefined,
|
|
806
|
+
heartbeatAt: undefined,
|
|
807
|
+
launchOwner: undefined,
|
|
808
|
+
launchRequestExpiresAt: undefined,
|
|
809
|
+
cancellationReason: truncateSummary(request.reason),
|
|
810
|
+
cancellation: status === 'cancelled' ? normalizeCancellation(request, current, now) : current.cancellation,
|
|
811
|
+
executionAttempts,
|
|
812
|
+
cancelledAt: current.cancelledAt || now,
|
|
813
|
+
terminalReason: status === 'cancelled' ? 'cancelled' : current.terminalReason,
|
|
814
|
+
recoveryHistory: status === 'cancelled' && !TERMINAL_STATUSES.has(current.status)
|
|
815
|
+
? appendRecoveryHistory(current, {
|
|
816
|
+
at: now,
|
|
817
|
+
action: 'cancelled',
|
|
818
|
+
reason: 'manual-cancel',
|
|
819
|
+
workerId: current.leaseOwner,
|
|
820
|
+
attempt: current.attempt,
|
|
821
|
+
detail: truncateSummary(request.reason),
|
|
822
|
+
})
|
|
823
|
+
: current.recoveryHistory,
|
|
824
|
+
};
|
|
825
|
+
store.runs[index] = updated;
|
|
826
|
+
this.save(store);
|
|
827
|
+
return { success: true, run: updated };
|
|
828
|
+
}
|
|
829
|
+
catch (err) {
|
|
830
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
recoverExpiredRuns(request = {}) {
|
|
834
|
+
try {
|
|
835
|
+
const now = typeof request.now === 'number' && Number.isFinite(request.now) ? request.now : Date.now();
|
|
836
|
+
const maxAttempts = Math.max(1, Math.min(20, Math.floor(request.maxAttempts || DEFAULT_MAX_ATTEMPTS)));
|
|
837
|
+
const runTimeoutMs = clampRunTimeoutMs(request.runTimeoutMs);
|
|
838
|
+
const staleHeartbeatMs = clampStaleHeartbeatMs(request.staleHeartbeatMs);
|
|
839
|
+
const store = this.load();
|
|
840
|
+
let recovered = 0;
|
|
841
|
+
let failed = 0;
|
|
842
|
+
let timedOut = 0;
|
|
843
|
+
let stale = 0;
|
|
844
|
+
const changed = [];
|
|
845
|
+
store.runs = store.runs.map((run) => {
|
|
846
|
+
if (activeRunTimedOut(run, now, runTimeoutMs)) {
|
|
847
|
+
assertStatusTransition(run.status, 'failed');
|
|
848
|
+
const detail = `Subagent run timed out after ${runTimeoutMs}ms.`;
|
|
849
|
+
const updated = {
|
|
850
|
+
...run,
|
|
851
|
+
status: 'failed',
|
|
852
|
+
updatedAt: now,
|
|
853
|
+
endedAt: run.endedAt || now,
|
|
854
|
+
leaseOwner: undefined,
|
|
855
|
+
leaseExpiresAt: undefined,
|
|
856
|
+
heartbeatAt: undefined,
|
|
857
|
+
launchOwner: undefined,
|
|
858
|
+
launchRequestExpiresAt: undefined,
|
|
859
|
+
error: truncateSummary(detail),
|
|
860
|
+
timedOutAt: run.timedOutAt || now,
|
|
861
|
+
terminalReason: 'timeout',
|
|
862
|
+
executionAttempts: markLatestAttempt(run, 'failed', now, { error: detail }),
|
|
863
|
+
recoveryHistory: appendRecoveryHistory(run, {
|
|
864
|
+
at: now,
|
|
865
|
+
action: 'failed',
|
|
866
|
+
reason: 'timeout',
|
|
867
|
+
workerId: run.leaseOwner,
|
|
868
|
+
attempt: run.attempt,
|
|
869
|
+
detail,
|
|
870
|
+
}),
|
|
871
|
+
};
|
|
872
|
+
failed += 1;
|
|
873
|
+
timedOut += 1;
|
|
874
|
+
changed.push(updated);
|
|
875
|
+
return updated;
|
|
876
|
+
}
|
|
877
|
+
const staleHeartbeat = activeHeartbeatStale(run, now, staleHeartbeatMs);
|
|
878
|
+
if (!activeLeaseExpired(run, now) && !staleHeartbeat)
|
|
879
|
+
return run;
|
|
880
|
+
const attempts = run.attempt || 0;
|
|
881
|
+
const terminal = attempts >= maxAttempts;
|
|
882
|
+
const status = terminal ? 'failed' : 'queued';
|
|
883
|
+
assertStatusTransition(run.status, status);
|
|
884
|
+
const reason = staleHeartbeat ? 'heartbeat-stale' : 'lease-expired';
|
|
885
|
+
const detail = terminal
|
|
886
|
+
? `Subagent ${staleHeartbeat ? 'heartbeat went stale' : 'lease expired'} after ${attempts} attempts.`
|
|
887
|
+
: `Recovered ${staleHeartbeat ? 'stale heartbeat' : 'expired lease'} from ${run.leaseOwner || 'unknown worker'}.`;
|
|
888
|
+
const updated = {
|
|
889
|
+
...run,
|
|
890
|
+
status,
|
|
891
|
+
updatedAt: now,
|
|
892
|
+
endedAt: terminal ? (run.endedAt || now) : undefined,
|
|
893
|
+
leaseOwner: undefined,
|
|
894
|
+
leaseExpiresAt: undefined,
|
|
895
|
+
heartbeatAt: undefined,
|
|
896
|
+
launchOwner: undefined,
|
|
897
|
+
launchRequestExpiresAt: undefined,
|
|
898
|
+
error: truncateSummary(detail),
|
|
899
|
+
terminalReason: terminal ? reason : run.terminalReason,
|
|
900
|
+
executionAttempts: markLatestAttempt(run, terminal ? 'failed' : 'expired', now, { error: detail }),
|
|
901
|
+
recoveryHistory: appendRecoveryHistory(run, {
|
|
902
|
+
at: now,
|
|
903
|
+
action: terminal ? 'failed' : 'requeued',
|
|
904
|
+
reason,
|
|
905
|
+
workerId: run.leaseOwner,
|
|
906
|
+
attempt: run.attempt,
|
|
907
|
+
detail,
|
|
908
|
+
}),
|
|
909
|
+
};
|
|
910
|
+
if (terminal)
|
|
911
|
+
failed += 1;
|
|
912
|
+
else
|
|
913
|
+
recovered += 1;
|
|
914
|
+
if (staleHeartbeat)
|
|
915
|
+
stale += 1;
|
|
916
|
+
changed.push(updated);
|
|
917
|
+
return updated;
|
|
918
|
+
});
|
|
919
|
+
if (changed.length > 0)
|
|
920
|
+
this.save(store);
|
|
921
|
+
return { success: true, recovered, failed, timedOut, stale, runs: changed };
|
|
922
|
+
}
|
|
923
|
+
catch (err) {
|
|
924
|
+
return { success: false, recovered: 0, failed: 0, timedOut: 0, stale: 0, runs: [], error: err instanceof Error ? err.message : String(err) };
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
getRun(id) {
|
|
928
|
+
try {
|
|
929
|
+
const runId = requiredString(id, 'id');
|
|
930
|
+
const run = this.load().runs.find((item) => item.id === runId);
|
|
931
|
+
if (!run)
|
|
932
|
+
return { success: false, error: 'Subagent run not found.' };
|
|
933
|
+
return { success: true, run };
|
|
934
|
+
}
|
|
935
|
+
catch (err) {
|
|
936
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
listRuns(request = {}) {
|
|
940
|
+
try {
|
|
941
|
+
const limit = Math.max(1, Math.min(Number(request.limit) || 100, 1000));
|
|
942
|
+
let runs = this.load().runs;
|
|
943
|
+
if (request.parentConversationId)
|
|
944
|
+
runs = runs.filter((run) => run.parentConversationId === request.parentConversationId);
|
|
945
|
+
if (request.parentAgentId)
|
|
946
|
+
runs = runs.filter((run) => run.parentAgentId === request.parentAgentId);
|
|
947
|
+
if (request.childConversationId)
|
|
948
|
+
runs = runs.filter((run) => run.childConversationId === request.childConversationId);
|
|
949
|
+
if (request.childAgentId)
|
|
950
|
+
runs = runs.filter((run) => run.childAgentId === request.childAgentId);
|
|
951
|
+
if (request.workspaceId)
|
|
952
|
+
runs = runs.filter((run) => run.workspaceId === request.workspaceId);
|
|
953
|
+
if (request.teamId)
|
|
954
|
+
runs = runs.filter((run) => run.teamId === request.teamId);
|
|
955
|
+
if (request.status)
|
|
956
|
+
runs = runs.filter((run) => run.status === request.status);
|
|
957
|
+
if (request.activeOnly)
|
|
958
|
+
runs = runs.filter((run) => ACTIVE_STATUSES.has(run.status));
|
|
959
|
+
return {
|
|
960
|
+
success: true,
|
|
961
|
+
runs: runs
|
|
962
|
+
.slice()
|
|
963
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
964
|
+
.slice(0, limit),
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
catch (err) {
|
|
968
|
+
return { success: false, runs: [], error: err instanceof Error ? err.message : String(err) };
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
getDurableRecoverySnapshot(request = {}) {
|
|
972
|
+
try {
|
|
973
|
+
const now = typeof request.now === 'number' && Number.isFinite(request.now) ? request.now : Date.now();
|
|
974
|
+
const runTimeoutMs = clampRunTimeoutMs(request.runTimeoutMs);
|
|
975
|
+
const staleHeartbeatMs = clampStaleHeartbeatMs(request.staleHeartbeatMs);
|
|
976
|
+
const runs = this.load().runs;
|
|
977
|
+
const queued = runs.filter((run) => run.status === 'queued');
|
|
978
|
+
const launchRequested = runs.filter((run) => run.status === 'launch-requested');
|
|
979
|
+
const running = runs.filter((run) => run.status === 'running');
|
|
980
|
+
const waiting = runs.filter((run) => run.status === 'waiting-for-permission' || run.status === 'waiting-for-user');
|
|
981
|
+
const terminal = runs.filter((run) => TERMINAL_STATUSES.has(run.status));
|
|
982
|
+
const recoverable = runs.filter((run) => activeLeaseExpired(run, now));
|
|
983
|
+
const timedOut = runs.filter((run) => activeRunTimedOut(run, now, runTimeoutMs));
|
|
984
|
+
const stale = runs.filter((run) => activeHeartbeatStale(run, now, staleHeartbeatMs));
|
|
985
|
+
const pending = runs.filter((run) => run.status === 'queued' || run.status === 'launch-requested');
|
|
986
|
+
return {
|
|
987
|
+
success: true,
|
|
988
|
+
snapshot: {
|
|
989
|
+
now,
|
|
990
|
+
queuedCount: queued.length,
|
|
991
|
+
launchRequestedCount: launchRequested.length,
|
|
992
|
+
runningCount: running.length,
|
|
993
|
+
waitingCount: waiting.length,
|
|
994
|
+
terminalCount: terminal.length,
|
|
995
|
+
activeRunIds: runs.filter((run) => ACTIVE_STATUSES.has(run.status)).map((run) => run.id),
|
|
996
|
+
pendingRunIds: pending.map((run) => run.id),
|
|
997
|
+
launchableRunIds: queued
|
|
998
|
+
.filter((run) => !recoverable.some((recoverableRun) => recoverableRun.id === run.id))
|
|
999
|
+
.map((run) => run.id),
|
|
1000
|
+
launchRequestedRunIds: launchRequested.map((run) => run.id),
|
|
1001
|
+
runningRunIds: running.map((run) => run.id),
|
|
1002
|
+
activeHeartbeats: runs
|
|
1003
|
+
.filter((run) => ACTIVE_STATUSES.has(run.status))
|
|
1004
|
+
.map((run) => ({
|
|
1005
|
+
runId: run.id,
|
|
1006
|
+
workerId: run.leaseOwner,
|
|
1007
|
+
heartbeatAt: run.heartbeatAt,
|
|
1008
|
+
leaseExpiresAt: run.leaseExpiresAt,
|
|
1009
|
+
})),
|
|
1010
|
+
recoverableRunIds: recoverable.map((run) => run.id),
|
|
1011
|
+
timedOutRunIds: timedOut.map((run) => run.id),
|
|
1012
|
+
staleRunIds: stale.map((run) => run.id),
|
|
1013
|
+
oldestQueuedAt: queued.reduce((oldest, run) => oldest === undefined || run.createdAt < oldest ? run.createdAt : oldest, undefined),
|
|
1014
|
+
oldestLaunchRequestedAt: launchRequested.reduce((oldest, run) => {
|
|
1015
|
+
const launchRequestedAt = run.launchRequestedAt || run.updatedAt;
|
|
1016
|
+
return oldest === undefined || launchRequestedAt < oldest ? launchRequestedAt : oldest;
|
|
1017
|
+
}, undefined),
|
|
1018
|
+
oldestRunningAt: running.reduce((oldest, run) => {
|
|
1019
|
+
const startedAt = run.startedAt || run.updatedAt;
|
|
1020
|
+
return oldest === undefined || startedAt < oldest ? startedAt : oldest;
|
|
1021
|
+
}, undefined),
|
|
1022
|
+
},
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
catch (err) {
|
|
1026
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
getAgentStatus(agentId) {
|
|
1030
|
+
try {
|
|
1031
|
+
const id = requiredString(agentId, 'agentId');
|
|
1032
|
+
const allRuns = this.load().runs.filter((run) => run.childAgentId === id);
|
|
1033
|
+
const activeRuns = allRuns.filter((run) => ACTIVE_STATUSES.has(run.status));
|
|
1034
|
+
return {
|
|
1035
|
+
success: true,
|
|
1036
|
+
status: {
|
|
1037
|
+
agentId: id,
|
|
1038
|
+
availability: activeRuns.length > 0 ? 'busy' : 'idle',
|
|
1039
|
+
activeRunCount: activeRuns.length,
|
|
1040
|
+
activeRunIds: activeRuns.map((run) => run.id),
|
|
1041
|
+
activeRuns,
|
|
1042
|
+
lastRunAt: allRuns.reduce((latest, run) => {
|
|
1043
|
+
if (latest === undefined || run.updatedAt > latest)
|
|
1044
|
+
return run.updatedAt;
|
|
1045
|
+
return latest;
|
|
1046
|
+
}, undefined),
|
|
1047
|
+
},
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
catch (err) {
|
|
1051
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
matchCapabilities(request) {
|
|
1055
|
+
try {
|
|
1056
|
+
return { success: true, capabilityMatch: matchSubagentCapabilities(request) };
|
|
1057
|
+
}
|
|
1058
|
+
catch (err) {
|
|
1059
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
load() {
|
|
1063
|
+
const primary = this.readStoreFile(this.storePath);
|
|
1064
|
+
if (primary.store)
|
|
1065
|
+
return primary.store;
|
|
1066
|
+
if (!existsSync(this.storePath))
|
|
1067
|
+
return { version: 1, runs: [] };
|
|
1068
|
+
this.preserveCorruptStore(this.storePath);
|
|
1069
|
+
const backup = this.readStoreFile(this.backupPath());
|
|
1070
|
+
if (backup.store) {
|
|
1071
|
+
this.atomicWrite(this.storePath, JSON.stringify(backup.store, null, 2));
|
|
1072
|
+
return backup.store;
|
|
1073
|
+
}
|
|
1074
|
+
return { version: 1, runs: [] };
|
|
1075
|
+
}
|
|
1076
|
+
save(store) {
|
|
1077
|
+
mkdirSync(dirname(this.storePath), { recursive: true });
|
|
1078
|
+
const current = this.readStoreFile(this.storePath);
|
|
1079
|
+
if (current.store) {
|
|
1080
|
+
copyFileSync(this.storePath, this.backupPath());
|
|
1081
|
+
}
|
|
1082
|
+
this.atomicWrite(this.storePath, JSON.stringify(store, null, 2));
|
|
1083
|
+
}
|
|
1084
|
+
readStoreFile(path) {
|
|
1085
|
+
try {
|
|
1086
|
+
if (!existsSync(path))
|
|
1087
|
+
return {};
|
|
1088
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
1089
|
+
if (parsed.version !== 1)
|
|
1090
|
+
return { error: 'Unsupported subagent run store version.' };
|
|
1091
|
+
if (!Array.isArray(parsed.runs))
|
|
1092
|
+
return { error: 'Subagent run store is missing runs array.' };
|
|
1093
|
+
if (!parsed.runs.every(isPersistedSubagentRun))
|
|
1094
|
+
return { error: 'Subagent run store contains malformed run records.' };
|
|
1095
|
+
return {
|
|
1096
|
+
store: {
|
|
1097
|
+
version: 1,
|
|
1098
|
+
runs: parsed.runs.map((run) => ({
|
|
1099
|
+
...run,
|
|
1100
|
+
recoveryHistory: normalizeRecoveryHistory(run.recoveryHistory),
|
|
1101
|
+
executionMetadata: normalizeRecordMetadata(run.executionMetadata),
|
|
1102
|
+
executionAttempts: normalizeExecutionAttempts(run.executionAttempts),
|
|
1103
|
+
})),
|
|
1104
|
+
},
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
catch (err) {
|
|
1108
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
backupPath() {
|
|
1112
|
+
return `${this.storePath}.bak`;
|
|
1113
|
+
}
|
|
1114
|
+
preserveCorruptStore(path) {
|
|
1115
|
+
try {
|
|
1116
|
+
if (!existsSync(path))
|
|
1117
|
+
return;
|
|
1118
|
+
const corruptPath = `${path}.corrupt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1119
|
+
copyFileSync(path, corruptPath);
|
|
1120
|
+
}
|
|
1121
|
+
catch {
|
|
1122
|
+
// Best effort only; callers still avoid overwriting a valid backup with corrupt data.
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
atomicWrite(path, contents) {
|
|
1126
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
1127
|
+
const tmpPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
1128
|
+
const fd = openSync(tmpPath, 'w');
|
|
1129
|
+
try {
|
|
1130
|
+
writeSync(fd, contents, 0, 'utf-8');
|
|
1131
|
+
fsyncSync(fd);
|
|
1132
|
+
}
|
|
1133
|
+
finally {
|
|
1134
|
+
closeSync(fd);
|
|
1135
|
+
}
|
|
1136
|
+
renameSync(tmpPath, path);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
let singleton = null;
|
|
1140
|
+
/**
|
|
1141
|
+
* The ONE subagent run store for this process.
|
|
1142
|
+
*
|
|
1143
|
+
* 🔴 A SINGLETON ON PURPOSE, and that is why the path is configured here rather than by
|
|
1144
|
+
* constructing a private instance. `SubagentScheduler` and `TeamAgentRouter` both default their
|
|
1145
|
+
* service parameter to this function, so a composition holding its own instance while they hold
|
|
1146
|
+
* the singleton would create runs in one store and launch them from another.
|
|
1147
|
+
*
|
|
1148
|
+
* ⚠️ The default path is the real user's home. A host running with its own home — a dev
|
|
1149
|
+
* profile, a test, CI — must pass `storePath` on the FIRST call, before anything else resolves
|
|
1150
|
+
* the singleton. Until 2026-08-18 nothing did, so an isolated host wrote its subagent runs into
|
|
1151
|
+
* the operator's actual `~/.xeno`, and two hosts shared one roster.
|
|
1152
|
+
*/
|
|
1153
|
+
/**
|
|
1154
|
+
* Drop the process-wide instance so the next `getSubagentRunService` can open a different home.
|
|
1155
|
+
*
|
|
1156
|
+
* 🔴 FOR TESTS ONLY, and the guard above is why it must exist rather than why it should not.
|
|
1157
|
+
* That guard is correct — two homes in one process is a real misconfiguration and silently serving
|
|
1158
|
+
* the wrong one would present as runs that vanish. But it left a suite that composes several hosts,
|
|
1159
|
+
* each with its own `mkdtemp` home, unable to run at all: `composeHostHeadless.test.ts` was RED from
|
|
1160
|
+
* the day the guard landed (2026-08-18), failing 9 of 11 cases with the guard's own error.
|
|
1161
|
+
*
|
|
1162
|
+
* ⚠️ Calling this from production code is the bug the guard exists to catch, not a way around
|
|
1163
|
+
* it — a running host has exactly one home for its whole life. It is exported separately, named
|
|
1164
|
+
* unmistakably, and touches nothing but the singleton reference.
|
|
1165
|
+
*/
|
|
1166
|
+
export function resetSubagentRunServiceForTests() {
|
|
1167
|
+
singleton = null;
|
|
1168
|
+
}
|
|
1169
|
+
export function getSubagentRunService(storePath) {
|
|
1170
|
+
if (!singleton) {
|
|
1171
|
+
singleton = new SubagentRunService(storePath);
|
|
1172
|
+
return singleton;
|
|
1173
|
+
}
|
|
1174
|
+
if (storePath && storePath !== singleton.storePath) {
|
|
1175
|
+
// Throws rather than returning the existing store. Two homes in one process is a real
|
|
1176
|
+
// misconfiguration, and silently handing back the wrong one is the split-brain this exists
|
|
1177
|
+
// to prevent — it would present as runs that vanish rather than as an error.
|
|
1178
|
+
throw new Error('The subagent run store is already open at a different path '
|
|
1179
|
+
+ `(${singleton.storePath}); refusing to serve ${storePath} from the same process.`);
|
|
1180
|
+
}
|
|
1181
|
+
return singleton;
|
|
1182
|
+
}
|
|
1183
|
+
//# sourceMappingURL=SubagentRunService.js.map
|